联盟(树的直径)
题意:
给你一棵树,你可以选择断开一条边再连上一条边使树的直径最小,求最小直径,可以断的边,任意一组可以断的点和新连的点。
题解:
算是求树上直径的综合运用。
先考虑一个问题:现在有两棵树,直径分别为$l_1$,$l_2$,
那么将两棵树合并后新树的直径最小为$\max(l_1,l_2,\lceil\frac{l_1}{2}\rceil+\lceil\frac{l_2}{2}\rceil+1)$。
显然合并时的最优选择为连上两棵树的中点,因为任何其他位置都不会使新树的直径更短。
本题中我们先dfs预处理出直径的两个端点,标记出一条直径。
然后分别以两个端点为根树形DP出每个子树内的最长链。
枚举断边,若断边在当前直径上,那么我们可以用预处理出的DP值求出新树的直径。
若不在直径上,那么一棵子树的直径为原直径,另一个子树的直径可用DP表示。
记录一下最小值以及取最小值的树的编号即可。
至于第三问,我们随便找到一条符合的边,找到切断它后两棵子树的直径的中点连边即可。
code:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define R register #define ll long long inline ll read() { ll aa=0;char cc=getchar(); while(cc<'0'||cc>'9')cc=getchar(); while(cc<='9'&&cc>='0') {aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();} return aa; } inline int max(int x,int y){return (x>y)?x:y;} const int N=300003,M=600003; struct edge{int u,v;}ed[N]; int tot,first[N],ver[M],nxt[M]; inline void add(int x,int y) { ver[++tot]=y;nxt[tot]=first[x]; first[x]=tot;return; } int n,f[N],fg[N],g[M],ve[N],ansi; void dp(int x,int fa,int ei) { f[x]=fg[x]=g[ei]=0; for(R int i=first[x],v;i;i=nxt[i]){ v=ver[i];if(v==fa)continue; dp(v,x,i); fg[x]=max(fg[x],f[x]+f[v]+1); g[ei]=max(g[ei],max(fg[v],f[x]+f[v]+1)); f[x]=max(f[x],f[v]+1); } fg[x]=max(fg[x],g[ei]); } int dis[N],pre[N],vi[N],pi,pj; void dfsi(int x,int fa) { for(R int i=first[x],v;i;i=nxt[i]){ v=ver[i];if(v==fa)continue; dis[v]=dis[x]+1;dfsi(v,x); pre[v]=x; if(dis[pj]<dis[v])pj=v; } } int dfsj(int x,int fa,int dep) { if(dep==0&&vi[x]==1)return x; for(R int i=first[x],v,y;i;i=nxt[i]){ v=ver[i];if(v==fa)continue; y=dfsj(v,x,dep-1); if(y)return y; } return 0; } void dfsk(int x,int fa,int lim) { for(R int i=first[x],v;i;i=nxt[i]){ v=ver[i]; if(v==fa||v==lim)continue; dis[v]=dis[x]+1; dfsk(v,x,lim); pre[v]=x; if(dis[pj]<dis[v])pj=v; } } void getline() { memset(vi,0,sizeof(vi)); int now=pj;vi[now]=1; while(now!=pi) now=pre[now],vi[now]=1; } void solve(int x,int y) { dis[x]=0;pj=x;dfsk(x,x,y);pi=pj; dis[pi]=0;dfsk(pi,pi,y); getline(); int u=dfsj(pi,pi,dis[pj]/2); dis[y]=0;pj=y;dfsk(y,y,x);pi=pj; dis[pi]=0;dfsk(pi,pi,x); getline(); int v=dfsj(pi,pi,dis[pj]/2); printf("%d %d %d %d\n",x,y,u,v); } int main() { //freopen("league.in","r",stdin); //freopen("league.out","w",stdout); n=read();tot=1; for(R int i=1,x,y;i<n;++i){ x=read();y=read(); add(x,y);add(y,x); ed[i]=(edge){x,y}; } dis[1]=0;pj=1;dfsi(1,1);pi=pj; dis[pi]=0;dfsi(pi,pi);ansi=dis[pj]; getline();//zhao dao zhi jing dp(pj,pj,1);dp(pi,pi,1); for(R int i=1,x,y,u,v,z;i<n;++i){ u=ed[i].u;v=ed[i].v; x=g[i<<1];y=g[i<<1|1]; if(vi[u]&&vi[v]) z=(x+1)/2+(y+1)/2+1; else z=dis[pj]; z=max(z,max(x,y)); if(z<ansi) ansi=z,ve[0]=0; if(z==ansi) ve[++ve[0]]=i; } printf("%d\n%d ",ansi,ve[0]); for(R int i=1;i<=ve[0];++i)printf("%d ",ve[i]);puts(""); solve(ed[ve[1]].u,ed[ve[1]].v);return 0; } /* 4 1 2 2 3 3 4 */