联盟(树的直径)

题意:

  给你一棵树,你可以选择断开一条边再连上一条边使树的直径最小,求最小直径,可以断的边,任意一组可以断的点和新连的点。

题解:

  算是求树上直径的综合运用。

  

  先考虑一个问题:现在有两棵树,直径分别为$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 
*/
posted @ 2019-09-24 10:29  Toot_Holmes  阅读(297)  评论(0编辑  收藏  举报