zoukankan      html  css  js  c++  java
  • 不回头的旅行

    Portal --> broken qwq

    Description

    ​  给你一棵树,点的编号从(1)(n),每个点有一个权值,每条边有一条边权,现在你要挑其中一个点作为起点,从这个点出发顺着树上的边走一条链,记录一个权值,该值一开始为(0),每次经过一条边就减去边权,到达一个点就加上点权,要求整个行走过程中记录的权值每时每刻都非负,求该链包含的点的数量的最大值

    ​  数据范围:(n<=10^5,1<=点权val_i<=10^4,1<=边权d<=10^4)

    Solution

    ​  暴(dian)力(fen)即是艺术!

    ​  第一眼看过去应该是。。树分治,于是考虑对于每一个当前的重心(rt)统计过(rt)这个点的可达的链的长度最大值

    ​  继续进行十分简单粗暴的思考,反正链的话就是某一个子树中走上来,然后再走进某一个子树中,(额接下来的描述可能会比较抽象一点,只是对算法的整个过程进行一个比较抽象的描述,具体会在后面细化)所以我们需要维护两个值,一个是从(rt)沿某个儿子往下走(这个计算过程记为(GoUp)),一个是从某个儿子的子树中往上走到(rt)(这个计算过程记为(GoDown)),有了这两个过程之后,我们可以考虑将(GoDown)中算出的需要补充的值存在一个数组(rec)中,然后在(GoUp)的时候一遍求值一边在(rec)中二分,然后更新答案

      

    ​  至于如何保证往上的链和往下的链不在同一个子树内,就直接是点分套路了,我们枚举每一个儿子,在(GoDown)过程中的(rec)数组一直累计之前枚举的儿子中的信息,这样处理一个儿子的时候,先(GoUp)(此时取到的往下走的链一定是之前的儿子的子树内的)再(GoDown)即可

    ​  那么现在的关键就是,要求每时每刻都非负,这个我们要如何判断呢?考虑行走的时候记录的权值的变化过程,如果说我们要走的边权大于出发的点权,那么此时我们手头上的权值一定要能填补两者之间的“空缺“,也就是要(>val-d),所以在(GoUp)(GoDown)过程中,最关键的就是判断当前的累计权值能否填补空缺

      

    ​  想清楚了统计的关键,接下来给出(rec)数组的定义:(rec[i])表示的是往下走的经过(i)个点的链需要填补的最小空缺,再具体一点就是需要补上多少才能使得(sum val-sum d>0)

    ​  首先来考虑(GoDown)过程,因为是顺着走的所以比较好判断,我们在(dfs)当前枚举到的儿子的子树的时候,直接记录从(rt)顺着走到当前节点(x)(sum val_i-sum d_i)即可,然后存到(rec)数组中,注意因为我们需要统计的答案是链上点数相关,所以(rec)按照遍历到的点数来存,然后因为我们需要关心的是走到当前需要填补多少”空缺“,所以如果说(sum val_i-sum d_i>=0)的话(rec)就用(0)更新就好了,否则就用(-(sum val_i-sum d_i))更新

      

    ​  接着是(GoUp)过程,这个过程会稍微麻烦一点,因为我们是逆着来走的,注意到当前算出来的(sum val_i-sum d_i>=0)并不代表往上走的时候每时每刻都是这样,所以我们需要特殊处理一下

    ​  考虑分段计算,具体什么意思呢?就是说一旦当前这段(sum val_i-sum d_i>=0),我们就二分一次更新答案,然后把这段“断开”,这样做能够保证每时每刻都非负是因为,如果说新加进来(val-d)之后变成负数了,那么一定要等到更深的地方加了一堆(val-d)之后整段权值非负才会更新答案,也就是保证了从下面走上来,断开的每一段都能保证每时每刻权值非负

    ​  具体实现的话就是除了(sum val_i-sum d_i)之外,我们再额外记一个值(tmp),一旦新开一段我们就把(tmp)赋成(0),然后继续累加即可

    ​  最后说一下更新答案的二分部分,当前往上走的链盈余出来的值就是(sum val_i-sum d_i),而我们的(rec)数组存的是需要补上的最小值,那。。直接二分咯

    ​  然后有一点要注意的是,我们的(rec)数组可能会出现。。点数多(也就是下标大)的反而需要填补的空缺小,因为可能出现。。走着走着突然遇到一个巨大的点权然后一夜暴富的情况,为了保证程序会继续往后找,我们每次更新完(rec)数组之后,每个位置都要取后面的最小值

      

      虽然说讲起来好像很啰嗦的样子不过想清楚了超级好写嗯qwq

      

    ​  代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define mp make_pair
    #define Pr pair<int,int>
    using namespace std;
    const int N=1e5+10,inf=2147483647;
    struct xxx{
    	int y,nxt,dis;
    }a[N*2];
    int h[N],sz[N],mx_sz[N],vis[N],rec[N],val[N];
    Pr rec_son[N];
    int rt,rt_sz,cntson,Sz;
    int n,m,tot,Ans;
    void add(int x,int y,int d){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].dis=d;}
    void get_sz(int fa,int x){
    	int u;
    	sz[x]=1; mx_sz[x]=0;
    	for (int i=h[x];i!=-1;i=a[i].nxt){
    		u=a[i].y;
    		if (u==fa||vis[u]) continue;
    		get_sz(x,u);
    		sz[x]+=sz[u];
    		mx_sz[x]=max(mx_sz[x],sz[u]);
    	}
    }
    void get_rt(int Rt,int fa,int x){
    	mx_sz[x]=max(mx_sz[x],sz[Rt]-sz[x]);
    	if (mx_sz[x]<rt_sz) rt=x,rt_sz=mx_sz[x];
    	int u;
    	for (int i=h[x];i!=-1;i=a[i].nxt){
    		u=a[i].y;
    		if (u==fa||vis[u]) continue;
    		get_rt(Rt,x,u);
    	}
    }
    void get_ans(int d,int rest){
    	int l=1,r=Sz+1,mid,ans=l;
    	while (l<=r){
    		mid=l+r>>1;
    		if (rec[mid]<=rest) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	if (rec[ans]<=rest)
    		Ans=max(Ans,ans+d);
    }
    void go_up(int fa,int x,int d,int now,int tmp){
    	if (tmp>=0){
    		get_ans(d,now);
    		tmp=0;
    	}
    	int u;
    	for (int i=h[x];i!=-1;i=a[i].nxt){
    		u=a[i].y;
    		if (u==fa||vis[u]) continue;
    		go_up(x,u,d+1,now+val[u]-a[i].dis,tmp+val[u]-a[i].dis);
    	}
    }
    void go_dw(int fa,int x,int d,int now,int mn){
    	if (mn>=0)
    		rec[d]=min(rec[d],0);
    	else
    		rec[d]=min(rec[d],-mn);//need to be filled
    	int u;
    	for (int i=h[x];i!=-1;i=a[i].nxt){
    		u=a[i].y;
    		if (u==fa||vis[u]) continue;
    		go_dw(x,u,d+1,now+val[x]-a[i].dis,min(mn,now+val[x]-a[i].dis));
    	}
    }
    void calc(int x){
    	int u,d;
    	get_sz(0,x);
    	cntson=0;
    	for (int i=h[x];i!=-1;i=a[i].nxt){
    		u=a[i].y;
    		if (vis[u]) continue;
    		rec_son[++cntson]=mp(u,a[i].dis);
    	}
    	Sz=sz[x]+1;
    	for (int i=1;i<=sz[x]+1;++i) rec[i]=inf;
    	rec[1]=0;//x
    	for (int i=1;i<=cntson;++i){
    		u=rec_son[i].first; d=rec_son[i].second;
    		go_up(0,u,1,val[u]-d,val[u]-d);//u
    		go_dw(0,u,2,val[x]-d,val[x]-d);//x u
    		for (int j=sz[x]+1;j>=1;--j) rec[i]=min(rec[i],rec[i+1]);
    	}
    
    	for (int i=1;i<=sz[x]+1;++i) rec[i]=inf;
    	rec[1]=0;//x
    	for (int i=cntson;i>=1;--i){
    		u=rec_son[i].first; d=rec_son[i].second;
    		go_up(0,u,1,val[u]-d,val[u]-d);//u
    		go_dw(0,u,2,val[x]-d,val[x]-d);//x u
    		for (int j=sz[x]+1;j>=1;--j) rec[i]=min(rec[i],rec[i+1]);
    	}
    }
    void solve(int x){
    	rt=-1; rt_sz=n+1;
    	int u;
    	get_sz(0,x);
    	get_rt(x,0,x);
    	vis[rt]=true;
    	calc(rt);
    	for (int i=h[rt];i!=-1;i=a[i].nxt){
    		u=a[i].y;
    		if (vis[u]) continue;
    		solve(u);
    	}
    }
    int read(){
    	int ret=0; char ch=getchar();
    	while(ch<'0'||ch>'9') ch=getchar();
    	while('0'<=ch&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return ret;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	int x,y,z;
    	n=read();
    	for (int i=1;i<=n;++i) val[i]=read();
    	memset(h,-1,sizeof(h));
    	tot=0;
    	for (int i=1;i<n;++i){
    		x=read(); y=read(); z=read();
    		add(x,y,z);
    		add(y,x,z);
    	}
    	solve(1);
    	printf("%d
    ",Ans);
    }
    
  • 相关阅读:
    娓娓道来c指针 (4)解析c的声明语句
    Snail—UI学习之UITextField
    E
    Qt录音机
    著名的英文搜索引擎
    java中Map,List与Set的差别
    Android图片处理:识别图像方向并显示
    Unity3D中组件事件函数的运行顺序
    Android屏幕density, dip等相关概念总结
    Codeforces Round #257 (Div. 2)
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9526629.html
Copyright © 2011-2022 走看看