zoukankan      html  css  js  c++  java
  • 联盟(树的直径)

    题意:

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

    题解:

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

      

      先考虑一个问题:现在有两棵树,直径分别为$l_1$,$l_2$,

      那么将两棵树合并后新树的直径最小为$max(l_1,l_2,lceilfrac{l_1}{2} ceil+lceilfrac{l_2}{2} ceil+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
    ",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
    %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 
    */
    
  • 相关阅读:
    【前端_js】前端跨网络异步获取资源——fetch()
    【前端_React】React小书
    【前端_js】JQuery DataTables插件的使用
    【前端_js】解决ajax跨域请求数据
    event.srcElement在火狐(FireFox)下的兼容问题。搜索框获得焦点时默认文字变化
    ASP.NET MVC 上传大文件时404
    使用Zen coding高效编写html代码
    CSS 去除列表项li前面的小圆点
    谈谈CSS的布局,display、position、float
    JS引用类型之——RegExp
  • 原文地址:https://www.cnblogs.com/toot-wjh/p/11576881.html
Copyright © 2011-2022 走看看