• [WC2018]通道——边分治+虚树+树形DP


    题目链接:

    [WC2018]通道

    题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和。

    我们按照部分分逐个分析有1、2、3棵树时的做法。

    首先说一个结论,在下面讲解中能应用到:

    对于一棵树T1的直径两端点为u,v,对于另一棵树T2的直径两端点为x,y,如果将两棵树合并(即将两棵树中的各一个点连边)那么新树的直径的两端点一定是u,v,x,y中的两个。

    证明见树的直径及其性质与证明

    一、一棵树

    这个很好做吧,直接求树的直径就好了。

    二、两棵树

    我们假设答案点对是(a,b),那么ans(a,b)=dis1(a,b)+dis2(a,b)。其中dis1,dis2分别表示两棵树上的两点距离。

    我们将第一棵树的答案拆开表示:ans(a,b)=dep1(a)+dep1(b)-2*dep1(lca)+dis2(a,b)。其中dep1为第一棵树中的该点深度。

    可以发现dep1(lca)与a,b无关,我们对于第二棵树上的点x建立一个点x'与x相连,边权为dep1(x)。

    这样忽略dep1(lca),答案就是Tree2中的直径。

    现在计算dep1(lca)对答案的影响,我们可以对于Tree1树形DP,每个点存子树中所有点在Tree2中的形成的直径的两端点及直径长度,回溯时将每个子节点的答案合并到父节点上,这时Tree2中的直径减掉这个父节点深度*2即可更新答案。合并时利用上面讲到的结论六种情况枚举讨论,更新答案时因为要保证直径两端点不在这个父节点的同一棵子树内,所以有四种情况可以更新答案。这里Tree1中一个点子树中所有点在Tree2中形成的直径可以看作是这些点在Tree2上两两之间路径包含的所有点组成的树的直径,而合并时相当于在Tree2上将两个可能有交集的子树拼接到一起,上述结论依旧成立。

    时间复杂度O(nlogn)。

    三、三棵树

    三棵树时答案为ans(a,b)=dis1(a,b)+dis2(a,b)+dis3(a,b)。

    我们对于第一棵树进行边分治,将多叉树转二叉树,对于每次分治的联通块,以中心边为界将联通块分成两部分。(边分治具体实现参见边分治讲解

    同时将第二棵树的答案拆开表示:ans(a,b)=d1(a)+d1(b)+val+dep2(a)+dep2(b)-2*dep2(lca)+dis3(a,b)。其中d1为该点到当前分治中心边的距离,val为分治中心边的长度。

    同样按照两棵树时的做法,对于第三棵树上的点x建一个点x'与x相连,边权为d1(x)+dep2(x)。

    每次对Tree1进行边分治时将分治联通块中的点在Tree2上建虚树,按照两棵树时的做法在虚树上树形DP。

    因为还需要保证在Tree3中找到的直径的端点在Tree1中分别位于分治中心边的两端,所以每次边分治将分治中心一边的点标号为1,另一边的点标号为2。

    在树形DP是每个点分别维护子树中标号为1/2的点组成的直径的两端点及直径长度,合并同样各种情况讨论一下并更新答案即可。

    时间复杂度为O(nlogn^2),RMQ求LCA+基数排序可以将时间复杂度降为O(nlogn)。

    注意:因为边权可能为0,所以求lca时不能比较真实深度(即带权深度)而要比较不带权深度。

    这道题也可以用点分治来代替边分治,但边分治能将每次分治联通块中的点恰好分成两部分,而点分治对于分治中心的处理比较麻烦,所以建议写边分治。

    亲测用树剖求LCA比用RMQ求LCA快,也不知道为什么...

    调了好几天,也写了好几个版本的,最后还是都坚持调出来了,虽然很长,但还是都放上来,供读者选择。

    其中edge_partation为第一棵树即边分治的树;virtual_tree为第二棵树即建虚树的树;value_tree为第三棵树即需要求直径的树。

    RMQ求LCA+非递归树形DP

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define pr pair<int,ll>
    #define INF 1<<30
    int n,m;
    int cnt;
    int x,y;
    ll ans,z;
    ll lty[100010];
    int col[100010];
    struct Miku
    {
    	int x;
    	ll dep;
    }t[400010];
    namespace value_tree
    {
    	int tot;
    	int dfn=0;
    	ll d[100010];
    	int s[100010];
    	ll val[200010];
    	int lg[200010];
    	int to[200010];
    	int head[100010];
    	int next[200010];
    	ll f[200010][19];
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		f[++dfn][0]=d[x];
    		s[x]=dfn;
    		lty[x]+=d[x];
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				d[to[i]]=d[x]+val[i];
    				dfs(to[i],x);
    				f[++dfn][0]=d[x];
    			}
    		}
    	}
    	inline void ST()
    	{
    		for(int i=2;i<=dfn;i++)
    		{
    			lg[i]=lg[i>>1]+1;
    		}
    		for(int j=1;j<=18;j++)
    		{
    			for(int i=1;i+(1<<j)-1<=dfn;i++)
    			{
    				f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	inline ll lca(int x,int y)
    	{
    		x=s[x],y=s[y];
    		if(x>y)
    		{
    			swap(x,y);
    		}
    		int len=lg[y-x+1];
    		return min(f[x][len],f[y-(1<<len)+1][len]);
    	}
    	inline ll dis(int x,int y)
    	{
    		return lty[x]+lty[y]-(lca(x,y)<<1);
    	}
    }
    namespace virtual_tree
    {
    	int tot;
    	int num;
    	int top;
    	int dfn=0;
    	int sum=0;
    	ll mid_edge;
    	ll d[100010];
    	int s[100010];
    	int l[100010];
    	int r[100010];
    	int q[200010];
    	ll val[200010];
    	int lg[200010];
    	int to[200010];
    	int st[200010];
    	int dep[100010];
    	int vis[100010];
    	int head[100010];
    	int next[200010];
    	int f[200010][19];
    	struct miku
    	{
    		int u,v;
    		ll len;
    		miku(){u=0,v=0,len=0;}
    		miku (const int& U,const int& V){u=U,v=V,len=value_tree::dis(u,v);}
    		miku (const int& U,const int& V,const ll& L){u=U,v=V,len=L;}
    		friend bool operator <(miku a,miku b){return a.len<b.len;}
    		friend miku operator +(miku a,miku b)
    		{
    			if(a.u==0)return b;
    			if(b.u==0)return a;
    			miku res=max(a,b);
    			res=max(res,max(miku(a.u,b.v),miku(a.v,b.u)));
    			res=max(res,max(miku(a.u,b.u),miku(a.v,b.v)));
    			return res;
    		}
    	}dp[100010][2];
    	bool cmp(int a,int b)
    	{
    		int x=a<0?r[-a]:l[a];
    		int y=b<0?r[-b]:l[b];
    		return x<y;
    	}
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		f[++dfn][0]=x;
    		s[x]=dfn;
    		l[x]=++sum;
    		lty[x]+=d[x];
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				dep[to[i]]=dep[x]+1;
    				d[to[i]]=d[x]+val[i];
    				dfs(to[i],x);
    				f[++dfn][0]=x;
    			}
    		}
    		r[x]=++sum;
    	}
    	inline int mn(int x,int y)
    	{
    		return dep[x]<dep[y]?x:y;
    	}
    	inline void ST()
    	{
    		for(int i=2;i<=dfn;i++)
    		{
    			lg[i]=lg[i>>1]+1;
    		}
    		for(int j=1;j<=18;j++)
    		{
    			for(int i=1;i+(1<<j)-1<=dfn;i++)
    			{
    				f[i][j]=mn(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	inline int lca(int x,int y)
    	{
    		x=s[x],y=s[y];
    		if(x>y)
    		{
    			swap(x,y);
    		}
    		int len=lg[y-x+1];
    		return mn(f[x][len],f[y-(1<<len)+1][len]);
    	}
    	inline ll merge(const miku& a,const miku& b)
    	{
    		if(a.u==0||b.u==0)return 0;
    		return max(max(value_tree::dis(a.u,b.u),value_tree::dis(a.v,b.u)),max(value_tree::dis(a.u,b.v),value_tree::dis(a.v,b.v)));
    	}
    	void tree_dp()
    	{
    		top=0;
    		for(int i=1;i<=tot;i++)
    		{
    			if(q[i]>0)
    			{
    				st[++top]=q[i];
    			}
    			else
    			{
    				top--;
    				if(!top)continue;
    				int fa=st[top];
    				int x=st[top+1];
    				ans=max(ans,max(merge(dp[x][0],dp[fa][1]),merge(dp[x][1],dp[fa][0]))+mid_edge-(d[fa]<<1));
    				dp[fa][0]=dp[fa][0]+dp[x][0];
    				dp[fa][1]=dp[fa][1]+dp[x][1];
    			}
    		}
    	}
    	inline void build(ll value)
    	{
    		mid_edge=value;
    		for(int i=1;i<=cnt;i++)
    		{
    			vis[t[i].x]=1;
    			dp[t[i].x][col[t[i].x]-1]=(miku){t[i].x,t[i].x,0};
    			dp[t[i].x][(col[t[i].x]-1)^1]=(miku){0,0,0};
    			q[i]=t[i].x;
    			col[t[i].x]=0;
    		}
    		num=tot=cnt;
    		sort(q+1,q+1+tot,cmp);
    		for(int i=1;i<num;i++)
    		{
    			int fa=lca(q[i],q[i+1]);
    			if(!vis[fa])
    			{
    				vis[fa]=1;
    				q[++tot]=fa;
    				dp[fa][0]=dp[fa][1]=(miku){0,0,0};
    			}
    		}
    		for(int i=1;i<=tot;i++)
    		{
    			vis[q[i]]=0;
    		}
    		num=tot;
    		for(int i=1;i<=num;i++)
    		{
    			q[++tot]=-q[i];
    		}
    		sort(q+1,q+1+tot,cmp);
    		tree_dp();
    	}
    }
    namespace edge_partation
    {
    	int tot;
    	int num;
    	int root;
    	int to[800010];
    	ll val[800010];
    	int vis[400010];
    	int next[800010];
    	int head[400010];
    	int size[400010];
    	vector<pr>q[400010];
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				q[x].push_back(make_pair(to[i],val[i]));
    				dfs(to[i],x);
    			}
    		}
    	}
    	inline void rebuild()
    	{
    		tot=1;
    		memset(head,0,sizeof(head));
    		memset(val,0,sizeof(val));
    		memset(next,0,sizeof(next));
    		memset(to,0,sizeof(to));
    		for(int i=1;i<=m;i++)
    		{
    			int len=q[i].size();
    			if(len<=2)
    			{
    				for(int j=0;j<len;j++)
    				{
    					add(i,q[i][j].first,q[i][j].second);
    					add(q[i][j].first,i,q[i][j].second);
    				}
    			}
    			else
    			{
    				int ls=++m;
    				int rs=++m;
    				add(i,ls,0);
    				add(ls,i,0);
    				add(i,rs,0);
    				add(rs,i,0);
    				for(int j=0;j<len;j++)
    				{
    					if(j&1)
    					{
    						q[ls].push_back(make_pair(q[i][j].first,q[i][j].second));
    					}
    					else
    					{
    						q[rs].push_back(make_pair(q[i][j].first,q[i][j].second));
    					}
    				}
    			}
    		}
    	}
    	void getroot(int x,int fa,int sum)
    	{
    		size[x]=1;
    		for(int i=head[x];i;i=next[i])
    		{
    			if(!vis[i>>1]&&to[i]!=fa)
    			{
    				getroot(to[i],x,sum);
    				size[x]+=size[to[i]];
    				int mx_size=max(size[to[i]],sum-size[to[i]]);
    				if(mx_size<num)
    				{
    					num=mx_size;
    					root=i;
    				}
    			}
    		}
    	}
    	void dfs2(int x,int fa,ll dep,int opt)
    	{
    		if(x<=n)
    		{
    			col[x]=opt;
    			t[++cnt]=(Miku){x,dep};
    		}
    		for(int i=head[x];i;i=next[i])
    		{
    			if(!vis[i>>1]&&to[i]!=fa)
    			{
    				dfs2(to[i],x,dep+val[i],opt);
    			}
    		}
    	}
    	void partation(int x,int sum)
    	{
    		num=INF;
    		getroot(x,0,sum);
    		if(num==INF)
    		{
    			return ;
    		}
    		int now=root;
    		vis[now>>1]=1;
    		cnt=0;
    		dfs2(to[now],0,0ll,1);
    		dfs2(to[now^1],0,0ll,2);
    		for(int i=1;i<=cnt;i++)
    		{
    			lty[t[i].x]+=t[i].dep;
    		}
    		virtual_tree::build(val[now]);
    		for(int i=1;i<=cnt;i++)
    		{
    			lty[t[i].x]-=t[i].dep;
    		}
    		int sz=size[to[now]];
    		partation(to[now],sz);
    		partation(to[now^1],sum-sz);
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	m=n;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		edge_partation::add(x,y,z);
    		edge_partation::add(y,x,z);
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		virtual_tree::add(x,y,z);
    		virtual_tree::add(y,x,z);
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		value_tree::add(x,y,z);
    		value_tree::add(y,x,z);
    	}
    	virtual_tree::dfs(1,0);
    	virtual_tree::ST();
    	value_tree::dfs(1,0);
    	value_tree::ST();
    	edge_partation::dfs(1,0);
    	edge_partation::rebuild();
    	edge_partation::partation(1,m);
    	printf("%lld",ans);
    }

    RMQ求LCA+递归树形DP

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define pr pair<int,ll>
    #define INF 1<<30
    int n,m;
    int cnt;
    int x,y;
    ll ans,z;
    ll lty[100010];
    int col[100010];
    struct Miku
    {
    	int x;
    	ll dep;
    }t[400010];
    namespace value_tree
    {
    	int tot;
    	int dfn=0;
    	ll d[100010];
    	int s[100010];
    	ll val[200010];
    	int lg[200010];
    	int to[200010];
    	int head[100010];
    	int next[200010];
    	ll f[200010][19];
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		f[++dfn][0]=d[x];
    		s[x]=dfn;
    		lty[x]+=d[x];
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				d[to[i]]=d[x]+val[i];
    				dfs(to[i],x);
    				f[++dfn][0]=d[x];
    			}
    		}
    	}
    	inline void ST()
    	{
    		for(int i=2;i<=dfn;i++)
    		{
    			lg[i]=lg[i>>1]+1;
    		}
    		for(int j=1;j<=18;j++)
    		{
    			for(int i=1;i+(1<<j)-1<=dfn;i++)
    			{
    				f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	inline ll lca(int x,int y)
    	{
    		x=s[x],y=s[y];
    		if(x>y)
    		{
    			swap(x,y);
    		}
    		int len=lg[y-x+1];
    		return min(f[x][len],f[y-(1<<len)+1][len]);
    	}
    	inline ll dis(int x,int y)
    	{
    		return lty[x]+lty[y]-(lca(x,y)<<1);
    	}
    }
    namespace virtual_tree
    {
    	int tot;
    	int top;
    	int sum=0;
    	int dfn=0;
    	ll mid_edge;
    	ll d[100010];
    	int l[100010];
    	int s[100010];
    	ll val[200010];
    	int lg[200010];
    	int to[200010];
    	int st[100010];
    	int vis[100010];
    	int dep[100010];
    	int head[100010];
    	int next[200010];
    	int f[200010][19];
    	vector<int>q[100010];
    	struct miku
    	{
    		int u,v;
    		ll len;
    		miku(){u=0,v=0,len=0;}
    		miku (const int& U,const int& V){u=U,v=V,len=value_tree::dis(u,v);}
    		miku (const int& U,const int& V,const ll& L){u=U,v=V,len=L;}
    		friend bool operator <(miku a,miku b){return a.len<b.len;}
    		friend miku operator +(miku a,miku b)
    		{
    			if(a.u==0)return b;
    			if(b.u==0)return a;
    			miku res=max(a,b);
    			res=max(res,max(miku(a.u,b.v),miku(a.v,b.u)));
    			res=max(res,max(miku(a.u,b.u),miku(a.v,b.v)));
    			return res;
    		}
    	}dp[100010][2];
    	bool cmp(Miku a,Miku b)
    	{
    		return l[a.x]<l[b.x];
    	}
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		f[++dfn][0]=x;
    		s[x]=dfn;
    		l[x]=++sum;
    		lty[x]+=d[x];
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				dep[to[i]]=dep[x]+1;
    				d[to[i]]=d[x]+val[i];
    				dfs(to[i],x);
    				f[++dfn][0]=x;
    			}
    		}
    	}
    	inline int mn(int x,int y)
    	{
    		return dep[x]<dep[y]?x:y;
    	}
    	inline void ST()
    	{
    		for(int i=2;i<=dfn;i++)
    		{
    			lg[i]=lg[i>>1]+1;
    		}
    		for(int j=1;j<=18;j++)
    		{
    			for(int i=1;i+(1<<j)-1<=dfn;i++)
    			{
    				f[i][j]=mn(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	inline int lca(int x,int y)
    	{
    		x=s[x],y=s[y];
    		if(x>y)
    		{
    			swap(x,y);
    		}
    		int len=lg[y-x+1];
    		return mn(f[x][len],f[y-(1<<len)+1][len]);
    	}
    	inline void insert(int x)
    	{
    		int fa=lca(x,st[top]);
    		if(!vis[fa])
    		{
    			vis[fa]=1;
    			dp[fa][0]=dp[fa][1]=(miku){0,0,0};
    		}
    		while(top>1&&dep[st[top-1]]>=dep[fa])
            {
                q[st[top-1]].push_back(st[top]);
                top--;
            }
            if(fa!=st[top])
            {
                q[fa].push_back(st[top]);
                st[top]=fa;
            }
            st[++top]=x;
    	}
    	inline ll merge(const miku& a,const miku& b)
    	{
    		if(a.u==0||b.u==0)return 0;
    		return max(max(value_tree::dis(a.u,b.u),value_tree::dis(a.v,b.u)),max(value_tree::dis(a.u,b.v),value_tree::dis(a.v,b.v)));
    	}
    	void tree_dp(int x)
    	{
    		int len=q[x].size();
    		for(int i=0;i<len;i++)
    		{
    			int to=q[x][i];
    			tree_dp(to);
    			ans=max(ans,max(merge(dp[x][0],dp[to][1]),merge(dp[x][1],dp[to][0]))+mid_edge-(d[x]<<1));
    			dp[x][0]=dp[x][0]+dp[to][0];
    			dp[x][1]=dp[x][1]+dp[to][1];
    		}
    		vis[x]=0;
    		q[x].clear();
    	}
    	inline void build(ll value)
    	{
    		mid_edge=value;
    		for(int i=1;i<=cnt;i++)
    		{
    			vis[t[i].x]=1;
    			dp[t[i].x][col[t[i].x]-1]=(miku){t[i].x,t[i].x,0};
    			dp[t[i].x][(col[t[i].x]-1)^1]=(miku){0,0,0};
    			col[t[i].x]=0;
    		}
    		sort(t+1,t+1+cnt,cmp);
    		top=0;
    		if(t[1].x!=1)
    		{
    			st[++top]=1;
    		}
    		for(int i=1;i<=cnt;i++)
    		{
    			insert(t[i].x);
    		}
    		while(top>1)
    		{
    			q[st[top-1]].push_back(st[top]);
    			top--;
    		}
    		tree_dp(1);
    	}
    }
    namespace edge_partation
    {
    	int tot;
    	int num;
    	int root;
    	int to[800010];
    	ll val[800010];
    	int vis[400010];
    	int next[800010];
    	int head[400010];
    	int size[400010];
    	vector<pr>q[400010];
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				q[x].push_back(make_pair(to[i],val[i]));
    				dfs(to[i],x);
    			}
    		}
    	}
    	inline void rebuild()
    	{
    		tot=1;
    		memset(head,0,sizeof(head));
    		for(int i=1;i<=m;i++)
    		{
    			int len=q[i].size();
    			if(len<=2)
    			{
    				for(int j=0;j<len;j++)
    				{
    					add(i,q[i][j].first,q[i][j].second);
    					add(q[i][j].first,i,q[i][j].second);
    				}
    			}
    			else
    			{
    				int ls=++m;
    				int rs=++m;
    				add(i,ls,0);
    				add(ls,i,0);
    				add(i,rs,0);
    				add(rs,i,0);
    				for(int j=0;j<len;j++)
    				{
    					if(j&1)
    					{
    						q[ls].push_back(make_pair(q[i][j].first,q[i][j].second));
    					}
    					else
    					{
    						q[rs].push_back(make_pair(q[i][j].first,q[i][j].second));
    					}
    				}
    			}
    		}
    	}
    	void getroot(int x,int fa,int sum)
    	{
    		size[x]=1;
    		for(int i=head[x];i;i=next[i])
    		{
    			if(!vis[i>>1]&&to[i]!=fa)
    			{
    				getroot(to[i],x,sum);
    				size[x]+=size[to[i]];
    				int mx_size=max(size[to[i]],sum-size[to[i]]);
    				if(mx_size<num)
    				{
    					num=mx_size;
    					root=i;
    				}
    			}
    		}
    	}
    	void dfs2(int x,int fa,ll dep,int opt)
    	{
    		if(x<=n)
    		{
    			col[x]=opt;
    			t[++cnt]=(Miku){x,dep};
    		}
    		for(int i=head[x];i;i=next[i])
    		{
    			if(!vis[i>>1]&&to[i]!=fa)
    			{
    				dfs2(to[i],x,dep+val[i],opt);
    			}
    		}
    	}
    	void partation(int x,int sum)
    	{
    		num=INF;
    		getroot(x,0,sum);
    		if(num==INF)
    		{
    			return ;
    		}
    		int now=root;
    		vis[now>>1]=1;
    		cnt=0;
    		dfs2(to[now],0,0ll,1);
    		dfs2(to[now^1],0,0ll,2);
    		for(int i=1;i<=cnt;i++)
    		{
    			lty[t[i].x]+=t[i].dep;
    		}
    		virtual_tree::build(val[now]);
    		for(int i=1;i<=cnt;i++)
    		{
    			lty[t[i].x]-=t[i].dep;
    		}
    		int sz=size[to[now]];
    		partation(to[now],sz);
    		partation(to[now^1],sum-sz);
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	m=n;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		edge_partation::add(x,y,z);
    		edge_partation::add(y,x,z);
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		virtual_tree::add(x,y,z);
    		virtual_tree::add(y,x,z);
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		value_tree::add(x,y,z);
    		value_tree::add(y,x,z);
    	}
    	virtual_tree::dfs(1,0);
    	virtual_tree::ST();
    	value_tree::dfs(1,0);
    	value_tree::ST();
    	edge_partation::dfs(1,0);
    	edge_partation::rebuild();
    	edge_partation::partation(1,m);
    	printf("%lld",ans);
    }

    树剖求LCA+递归树形DP

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define pr pair<int,ll>
    #define INF 1<<30
    int n,m;
    int cnt;
    int x,y;
    ll ans,z;
    int lg[200010];
    ll lty[100010];
    int col[100010];
    struct Miku
    {
    	int x;
    	ll dep;
    }t[400010];
    namespace value_tree
    {
    	int tot;
    	int dfn;
    	ll d[100010];
    	int s[100010];
    	ll val[200010];
    	int to[200010];
    	int head[100010];
    	int next[200010];
    	ll f[200010][19];
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		f[++dfn][0]=d[x];
    		s[x]=dfn;
    		lty[x]+=d[x];
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				d[to[i]]=d[x]+val[i];
    				dfs(to[i],x);
    				f[++dfn][0]=d[x];
    			}
    		}
    	}
    	inline void ST()
    	{
    		for(int i=2;i<=dfn;i++)
    		{
    			lg[i]=lg[i>>1]+1;
    		}
    		for(int j=1;j<=18;j++)
    		{
    			for(int i=1;i+(1<<j)-1<=dfn;i++)
    			{
    				f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	inline ll lca(int x,int y)
    	{
    		x=s[x],y=s[y];
    		if(x>y)
    		{
    			swap(x,y);
    		}
    		int len=lg[y-x+1];
    		return min(f[x][len],f[y-(1<<len)+1][len]);
    	}
    	inline ll dis(int x,int y)
    	{
    		return lty[x]+lty[y]-(lca(x,y)<<1);
    	}
    }
    namespace virtual_tree
    {
    	int tot;
    	int top;
    	int num=0;
    	ll mid_edge;
    	ll d[100010];
    	int f[100010];
    	int s[100010];
    	ll val[200010];
    	int to[200010];
    	int st[100010];
    	int vis[100010];
    	int anc[100010];
    	int son[100010];
    	int dep[100010];
    	int size[100010];
    	int head[100010];
    	int next[200010];
    	vector<int>q[100010];
    	struct miku
    	{
    		int u,v;
    		ll len;
    		miku(){u=0,v=0,len=0;}
    		miku (const int& U,const int& V){u=U,v=V,len=value_tree::dis(u,v);}
    		miku (const int& U,const int& V,const int& L){u=U,v=V,len=L;}
    		friend bool operator <(miku a,miku b){return a.len<b.len;}
    		friend miku operator +(miku a,miku b)
    		{
    			if(a.u==0)return b;
    			if(b.u==0)return a;
    			miku res=max(a,b);
    			res=max(res,max(miku(a.u,b.v),miku(a.v,b.u)));
    			res=max(res,max(miku(a.u,b.u),miku(a.v,b.v)));
    			return res;
    		}
    	}dp[100010][2];
    	bool cmp(Miku a,Miku b)
    	{
    		return s[a.x]<s[b.x];
    	}
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x)
    	{
    		dep[x]=dep[f[x]]+1;
    		size[x]=1;
    		lty[x]+=d[x];
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=f[x])
    			{
    				f[to[i]]=x;
    				d[to[i]]=d[x]+val[i];
    				dfs(to[i]);
    				size[x]+=size[to[i]];
    				if(size[to[i]]>size[son[x]])
    				{
    					son[x]=to[i];
    				}
    			}
    		}
    	}
    	void dfs2(int x,int tp)
    	{
    		s[x]=++num;
    		anc[x]=tp;
    		if(son[x])
    		{
    			dfs2(son[x],tp);
    		}
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=f[x]&&to[i]!=son[x])
    			{
    				dfs2(to[i],to[i]);
    			}
    		}
    	}
    	inline int lca(int x,int y)
    	{
    		while(anc[x]!=anc[y])
    		{
    			if(dep[anc[x]]<dep[anc[y]])
    			{
    				swap(x,y);
    			}
    			x=f[anc[x]];
    		}
    		return dep[x]<dep[y]?x:y;
    	}
    	inline void insert(int x)
    	{
    		int fa=lca(x,st[top]);
    		if(!vis[fa])
    		{
    			vis[fa]=1;
    			dp[fa][0]=dp[fa][1]=(miku){0,0,0};
    		}
    		while(top>1&&dep[st[top-1]]>=dep[fa])
    		{
    			q[st[top-1]].push_back(st[top]);
    			top--;
    		}
    		if(fa!=st[top])
    		{
    			q[fa].push_back(st[top]);
    			st[top]=fa;
    		}
    		st[++top]=x;
    	}
    	inline ll merge(const miku& a,const miku& b)
    	{
    		if(a.u==0||b.u==0)return 0;
    		return max(max(value_tree::dis(a.u,b.u),value_tree::dis(a.v,b.u)),max(value_tree::dis(a.u,b.v),value_tree::dis(a.v,b.v)));
    	}
    	void tree_dp(int x)
    	{
    		int len=q[x].size();
    		for(int i=0;i<len;i++)
    		{
    			int to=q[x][i];
    			tree_dp(to);
    			ans=max(ans,max(merge(dp[x][0],dp[to][1]),merge(dp[x][1],dp[to][0]))+mid_edge-2*d[x]);
    			dp[x][0]=dp[x][0]+dp[to][0];
    			dp[x][1]=dp[x][1]+dp[to][1];
    		}
    		vis[x]=0;
    		q[x].clear();
    	}
    	inline void build(ll value)
    	{
    		mid_edge=value;
    		for(int i=1;i<=cnt;i++)
    		{
    			vis[t[i].x]=1;
    			dp[t[i].x][col[t[i].x]-1]=(miku){t[i].x,t[i].x,0};
    			dp[t[i].x][(col[t[i].x]-1)^1]=(miku){0,0,0};
    			col[t[i].x]=0;
    		}
    		sort(t+1,t+1+cnt,cmp);
    		top=0;
    		if(t[1].x!=1)
    		{
    			st[++top]=1;
    		}
    		for(int i=1;i<=cnt;i++)
    		{
    			insert(t[i].x);
    		}
    		while(top>1)
    		{
    			q[st[top-1]].push_back(st[top]);
    			top--;
    		}
    		tree_dp(1);
    	}
    }
    namespace edge_partation
    {
    	int tot;
    	int num;
    	int root;
    	int to[800010];
    	ll val[800010];
    	int vis[400010];
    	int next[800010];
    	int head[400010];
    	int size[400010];
    	vector<pr>q[400010];
    	inline void add(int x,int y,ll z)
    	{
    		next[++tot]=head[x];
    		head[x]=tot;
    		to[tot]=y;
    		val[tot]=z;
    	}
    	void dfs(int x,int fa)
    	{
    		for(int i=head[x];i;i=next[i])
    		{
    			if(to[i]!=fa)
    			{
    				q[x].push_back(make_pair(to[i],val[i]));
    				dfs(to[i],x);
    			}
    		}
    	}
    	inline void rebuild()
    	{
    		tot=1;
    		memset(head,0,sizeof(head));
    		for(int i=1;i<=m;i++)
    		{
    			int len=q[i].size();
    			if(len<=2)
    			{
    				for(int j=0;j<len;j++)
    				{
    					add(i,q[i][j].first,q[i][j].second);
    					add(q[i][j].first,i,q[i][j].second);
    				}
    			}
    			else
    			{
    				int ls=++m;
    				int rs=++m;
    				add(i,ls,0);
    				add(ls,i,0);
    				add(i,rs,0);
    				add(rs,i,0);
    				for(int j=0;j<len;j++)
    				{
    					if(j&1)
    					{
    						q[ls].push_back(make_pair(q[i][j].first,q[i][j].second));
    					}
    					else
    					{
    						q[rs].push_back(make_pair(q[i][j].first,q[i][j].second));
    					}
    				}
    			}
    		}
    	}
    	void getroot(int x,int fa,int sum)
    	{
    		size[x]=1;
    		for(int i=head[x];i;i=next[i])
    		{
    			if(!vis[i>>1]&&to[i]!=fa)
    			{
    				getroot(to[i],x,sum);
    				size[x]+=size[to[i]];
    				int mx_size=max(size[to[i]],sum-size[to[i]]);
    				if(mx_size<num)
    				{
    					num=mx_size;
    					root=i;
    				}
    			}
    		}
    	}
    	void dfs2(int x,int fa,ll dep,int opt)
    	{
    		if(x<=n)
    		{
    			col[x]=opt;
    			t[++cnt]=(Miku){x,dep};
    		}
    		for(int i=head[x];i;i=next[i])
    		{
    			if(!vis[i>>1]&&to[i]!=fa)
    			{
    				dfs2(to[i],x,dep+val[i],opt);
    			}
    		}
    	}
    	void partation(int x,int sum)
    	{
    		num=INF;
    		getroot(x,0,sum);
    		if(num==INF)
    		{
    			return ;
    		}
    		int now=root;
    		vis[now>>1]=1;
    		cnt=0;
    		dfs2(to[now],0,0ll,1);
    		dfs2(to[now^1],0,0ll,2);
    		for(int i=1;i<=cnt;i++)
    		{
    			lty[t[i].x]+=t[i].dep;
    		}
    		virtual_tree::build(val[now]);
    		for(int i=1;i<=cnt;i++)
    		{
    			lty[t[i].x]-=t[i].dep;
    		}
    		int sz=size[to[now]];
    		partation(to[now],sz);
    		partation(to[now^1],sum-sz);
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	m=n;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		edge_partation::add(x,y,z);
    		edge_partation::add(y,x,z);
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		virtual_tree::add(x,y,z);
    		virtual_tree::add(y,x,z);
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		value_tree::add(x,y,z);
    		value_tree::add(y,x,z);
    	}
    	virtual_tree::dfs(1);
    	virtual_tree::dfs2(1,1);
    	value_tree::dfs(1,0);
    	value_tree::ST();
    	edge_partation::dfs(1,0);
    	edge_partation::rebuild();
    	edge_partation::partation(1,m);
    	printf("%lld",ans);
    }
  • 相关阅读:
    焦点Banner效果滚动
    缓冲效果
    招商银行购物网站的产品展示效果,循环播放
    神是怎么看待计算机的呢
    Liaoning Province保三成功
    JAR文件(文件格式)
    tamarin系列之5] 植入本地方法实现
    无线上把锁:WEP、WPA无线加密方式对比
    Tamarin
    V8 Javascript 引擎设计理念
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10166723.html
Copyright © 2020-2023  润新知