[BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]

题意

给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值。

\(n\leq 5\times 10^5\) .

分析

  • 记断掉一条边之后两棵树的直径为 \(A,B\) ,最长直径为 \(A+B+1\) 最短为 \(\max\{A\ ,B\ ,\lceil \frac{A}{2}\rceil+\lceil \frac{B}{2} \rceil +1\}\) .

  • 维护每个点不同子树的前3长链和向上的最长链、不同子树的前2长路径和向上子树的最长路径。

  • 这样枚举断掉一条边之后换根 \(dp\) 一下就可以求得父节点所在树的直径了。

  • 方案可以按照 \(dfs\) 找树的直径的方式构造答案。

  • 总时间复杂度为 \(O(n)\)

换根dp可以解决树断一条边以及不同根询问等问题。

代码

#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
#define re(x) memset(x,0,sizeof x)
typedef long long LL;
inline int gi(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
	return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=5e5 + 7,inf=0x3f3f3f3f;
int n,edc=1,edg1,edg2,ans1=inf,ans2,ban[N<<1];
int f1[N][3],f2[N],g1[N][2],g2[N],fu[N],f[N][4],g[N][3],head[N];
struct edge{
	int last,to;
	edge(){}edge(int last,int to):last(last),to(to){}
}e[N*2];
void Add(int a,int b){
	e[++edc]=edge(head[a],b),head[a]=edc;
	e[++edc]=edge(head[b],a),head[b]=edc;
}
bool cmp(int x,int y){return x>y;}
void dfs1(int u,int fa){
	go(u)if(v^fa){
		dfs1(v,u);
		int x=fu[v];
		if(x>g1[u][0]) g1[u][1]=g1[u][0],g1[u][0]=x;
		else if(x>g1[u][1]) g1[u][1]=x;
		
		x=f1[v][0]+1;
		if(x>f1[u][0]) f1[u][2]=f1[u][1],f1[u][1]=f1[u][0],f1[u][0]=x;
		else if(x>f1[u][1]) f1[u][2]=f1[u][1],f1[u][1]=x;
		else if(x>f1[u][2]) f1[u][2]=x;
	}
	fu[u]=max(g1[u][0],f1[u][0]+f1[u][1]);
}
void dfs2(int u,int fa){
	for(int i=0;i<3;++i) f[u][i]=f1[u][i];f[u][3]=f2[u];sort(f[u],f[u]+4,cmp);
	for(int i=0;i<2;++i) g[u][i]=g1[u][i];g[u][2]=g2[u];sort(g[u],g[u]+3,cmp);
	go(u)if(v^fa){
		int x=0,cnt=0;
		for(int j=0,fg=0;j<4;++j){
			if(f[u][j]==f1[v][0]+1&&!fg){fg=1;continue;}
			x=f[u][j]; break;
		}
		f2[v]=x+1,x=0;
		for(int j=0,fg=0;j<4;++j){
			if(f[u][j]==f1[v][0]+1&&!fg) {fg=1;continue;}
			x+=f[u][j];if(++cnt==2) break;
		}
		
		for(int j=0,fg=0;j<3;++j){
			if(g[u][j]==fu[v]&&!fg) {fg=1;continue;}
		    Max(x,g[u][j]);break;
		}
		g2[v]=x;
		
		int tmp=max(max(x,fu[v]),(x+1)/2+(fu[v]+1)/2+1);
		if(ans1>tmp) ans1=tmp,edg1=i;
		
		tmp=(x+fu[v]+1);
		if(ans2<tmp) ans2=tmp,edg2=i;
		
		dfs2(v,u);
	}
}
int fa[N],mx,cho,tp,tmp[N];
void dfs(int u,int dis){
	if(dis>=mx) mx=dis,cho=u;
	go(u)if(v^fa[u]&&!ban[i]) fa[v]=u,dfs(v,dis+1);
}
int main(){
	n=gi();
	rep(i,1,n-1) Add(gi(),gi());
	dfs1(1,0); dfs2(1,0);
	
	printf("%d %d %d",ans1,e[edg1].to,e[edg1^1].to);
	ban[edg1]=ban[edg1^1]=1;
	int x=e[edg1].to;dfs(x,0);
	re(fa),x=cho,mx=0,dfs(x,0);tp=0;for(int i=cho;i;i=fa[i]) tmp[++tp]=i;printf(" %d",tmp[tp+1>>1]);
	
	re(fa),x=e[edg1^1].to,mx=0,dfs(x,0);
	re(fa),x=cho,mx=0,dfs(x,0);tp=0;for(int i=cho;i;i=fa[i]) tmp[++tp]=i;printf(" %d\n",tmp[tp+1>>1]);
	
	printf("%d %d %d",ans2,e[edg2].to,e[edg2^1].to);
	ban[edg1]=ban[edg1^1]=0,ban[edg2]=ban[edg2^1]=1;
	
	re(fa),x=e[edg2].to,mx=0,dfs(x,0);printf(" %d",cho);
	re(fa),x=e[edg2^1].to,mx=0,dfs(x,0);printf(" %d\n",cho);
	return 0;
}
posted @ 2018-10-23 19:29  fwat  阅读(286)  评论(0编辑  收藏  举报