zoukankan      html  css  js  c++  java
  • 【CSP-S2019D2T3】树的重心

    Description

    在这里插入图片描述
    在这里插入图片描述

    Solution

    • 考场上没有足够的时间,也并没有打二叉树的,所以只有55分了。。。。
    • 首先树的重心有几个性质我居然一直不知道!(虽然题面里面都有提及,但是我居然将它们忽略了)。
      1.树的重心当且仅当最大的子树大小小于n/2。如果不是的话可以通过往大于n/2的子树中调整获得更优的答案。再推一下可以发现,当这个时候的重心的最大子树的size为n/2且n为偶数的时候,这个儿子也是一个重心,因此重心只有一个或两个。
      2.树的重心一定在包括根节点的重链上。首先求重心有一种我之前一直没有用过的方法,就是从根节点往下逼近,那么如果在逼近的过程中如果往轻儿子走的话,当前最大重儿子的最小size的最大值就己经没有往重儿子走优。
      3.根据上面的两个性质,可以得知只需要找到重链上最后一个sz>=n/2的,那么在这个重链以前的都一定满足第一个条件,又因为只有一个或两个,所以取末尾两个对比一下就好哦了。
    • 可以用倍增维护重链。
    • 换根转移即可。
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define maxn 300005
    #define maxp 20
    #define mem(a) memset(a,0,sizeof(a))
    #define ll long long 
    using namespace std;
    
    int T,n,i,j,k,x,y;
    int em,e[maxn*2],nx[maxn*2],ls[maxn];
    int fa[maxn],sz[maxn],gs[maxn];
    int Sz[maxn],Gs[maxn],f[maxn][maxp],F[maxn][maxp];
    ll ans;
    
    void read(int &x){
    	x=0; char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar());
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    }
    
    void insert(int x,int y){
    	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
    	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
    }
    
    void DFS(int x,int p){
    	sz[x]=1,fa[x]=p;
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
    		DFS(e[i],x),sz[x]+=sz[e[i]];
    		if (!gs[x]||sz[gs[x]]<sz[e[i]]) 	
    			gs[x]=e[i];
    	}
    	f[x][0]=gs[x],Gs[x]=gs[x];
    	for(int i=1;i<maxp;i++) f[x][i]=f[f[x][i-1]][i-1];
    }
    
    void upd(int x){
    	for(int i=1;i<maxp;i++) 	
    		F[x][i]=F[F[x][i-1]][i-1];
    }
    
    void Get(int st){
    	int N=Sz[st];
    	int y1=st,y2=st,c=0;
    	for(int i=maxp-1;i>=0;i--) if (F[y1][i]&&Sz[F[y1][i]]>=(N+1)/2)
    		y1=F[y1][i],c+=1<<i;
    	if (c){
    		c--;
    		for(int i=maxp-1;i>=0;i--) if (c>=(1<<i))
    			c-=1<<i,y2=F[y2][i];
    		int mx=min(max(N-Sz[y1],Sz[Gs[y1]]),max(N-Sz[y2],Sz[Gs[y2]]));
    		if (max(N-Sz[y1],Sz[Gs[y1]])==mx) ans+=y1;
    		if (max(N-Sz[y2],Sz[Gs[y2]])==mx) ans+=y2;
    	} else ans+=y1;
    }
    
    int totT,To[maxn],pre[maxn],nex[maxn];
    void DFS2(int x,int p){
    	if (x!=1) Get(x);
    	totT=0;
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p) To[++totT]=e[i];
    	int mx=To[1];
    	for(int i=2;i<=totT;i++){
    		pre[To[i]]=mx;
    		if (Sz[To[i]]>Sz[mx]) mx=To[i];
    	}
    	mx=To[totT];
    	for(int i=totT-1;i>=1;i--){
    		nex[To[i]]=mx;
    		if (Sz[To[i]]>Sz[mx]) mx=To[i];
    	}
    	
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
    		Gs[x]=fa[x];
    		if (pre[e[i]]&&Sz[pre[e[i]]]>Sz[Gs[x]]) Gs[x]=pre[e[i]];
    		if (nex[e[i]]&&Sz[nex[e[i]]]>Sz[Gs[x]]) Gs[x]=nex[e[i]];
    		F[x][0]=Gs[x],upd(x);
    		Sz[x]=n-Sz[e[i]];
    		Get(x);
    		DFS2(e[i],x);
    	}
    	Sz[x]=sz[x],Gs[x]=gs[x];
    	memcpy(F[x],f[x],sizeof(f[x]));
    }
    
    int main(){
    	freopen("centroid.in","r",stdin);
    	freopen("centroid.out","w",stdout);
    	read(T);
    	while (T--){
    		read(n),ans=0;
    		em=0,mem(ls),mem(f),mem(sz),mem(gs),mem(pre),mem(nex);
    		for(i=1;i<n;i++) read(x),read(y),insert(x,y);
    		DFS(1,0);
    		memcpy(Sz,sz,sizeof(sz));
    		memcpy(F,f,sizeof(F));
    		DFS2(1,0);
    		printf("%lld
    ",ans);
    	}
    }
    
    
  • 相关阅读:
    一、left
    padding溢出
    一、
    Python创建、删除桌面、启动组快捷方式的例子分享
    openstack常见问题解决方法总结
    __attribute__ 详解
    __ATTRIBUTE__ 知多少?
    CentOS如何设置终端显示字符界面区域的大小
    shell使用技巧
    openstack 安全策略权限控制等api接口
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090906.html
Copyright © 2011-2022 走看看