树的重心

重心

树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。

树的重心定义为树的某个节点,当去掉该节点后,树的各个连通分量中,节点数最多的连通分量其节点数达到最小值。树可能存在多个重心。如下图,当去掉点1后,树将分成两个连通块:(2,4,5),(3,6,7),则最大的连通块包含节点个数为3。若去掉点2,则树将分成3个部分,(4),(5),(1,3,6,7)最大的连通块包含4个节点;第一种方法可以得到更小的最大联通分量。可以发现,其他方案不可能得到比3更小的值了。所以,点1是树的重心。

性质

1.树上所有的点到树的重心的距离之和是最短的,如果有多个重心,那么总距离相等。

2.把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。

3.一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

4.一棵树最多有两个重心,且相邻。

算法分析

和树的最大独立问题类似,先任选一个结点作为根节点,把无根树变成有根树,然后设\(d[i]\)表示以\(i\)为根的子树的结点的个数。不难发现 \(d[i]=∑d[j]+1,j∈s[i]\)\(s[i]\)\(i\)结点的所有儿子结点的编号的集合。程序也十分简单:只需要DFS一次,在无根树转有根数的同时计算即可,连记忆化都不需要——因为本来就没有重复计算。
那么,删除结点i后,最大的连通块有多少个呢?结点i的子树中最大有\(max(d[j])\)个结点,i的“上方子树”中有\(n-d(i)\)个结点,这样,在动态规划的过程中就可 以顺便找出树的重心了。

【模板】树的重心

#include<bits/stdc++.h>
using namespace std;

const int N=3e5;
int head[N],ne[N],value[N],st[N],idx=0,ans=N,n;
//head数组用来记录每一条链中的头节点,ne相当于next指针,value相当于值域
//st数组用来记录dfs时是否被访问过

void add_to_head(int a,int b)
{
    value[idx]=b;
    ne[idx]=head[a];
    head[a]=idx++;
}

//以u为根的子树中节点的数量
int dfs(int u)
{
    int sum=0,res=0;  
    //sum表示当前这个子树的大小,res用来存去掉这个节点后所有连通块中的最大值
    st[u]=1;    //用来记录已经被搜过了
    for(int i=head[u];i!=-1;i=ne[i])
    {
        int j=value[i];
        if(!st[j]) 
        {
            int s=dfs(j); //s用来表示当前子树的大小
            res=max(s,res);
            sum+=s;
        }
    }

    res=max(res,n-sum-1);

    ans=min(ans,res);

    return sum+1;
}

int main()
{
    cin>>n;
    memset(head,-1,sizeof(head));
    for(int i=0;i<n-1;i++)
    {
        int a,b;
        cin>>a>>b;
        add_to_head(a,b),add_to_head(b,a);
    }
    dfs(1);
    cout<<ans<<endl;
}

P5666 [CSP-S2019] 树的重心

#include<bits/stdc++.h>
#define ll long long 
#define ano ((i-1)^1)+1
using namespace std;
const int N=5e5;
int T,n,tot,root,son2,nowans;
ll ans;
int head[N],ver[2*N],Next[2*N],siz[N],son[N],ans1[N],ans2[N],ans3[N],ffa[N],d[N],od[N],fa[N];
void add(int x,int y)
{
	ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
	ver[++tot]=x,Next[tot]=head[y],head[y]=tot;
}
void findroot(int x,int f)
{
	siz[x]=1;
	for(int i=head[x],y=ver[i];i;i=Next[i],y=ver[i])
		if(y!=f)
		{
			findroot(y,x);
			siz[x]+=siz[y];
			if(siz[y]>siz[son[x]])
				son[x]=y;
		}
	if(siz[son[x]]*2<=n && (n-siz[x])*2<=n)
		root=x;
}
void pre(int x,int f)
{
	siz[x]=1,d[x]=d[f]+1,fa[x]=f;
	if(f==root)
		ffa[x]=x;
	else
		ffa[x]=ffa[f];
	for(int i=head[x],y=ver[i];i;i=Next[i],y=ver[i])
		if(y!=f)
		{
			pre(y,x);
			siz[x]+=siz[y];
			if(siz[y]>siz[son[x]])
			{
				if(x==root)
					son2=son[x];
				son[x]=y;
			}
			else
				if(x==root && siz[y]>siz[son2])
					son2=y;
		}
}
void get1(int x,int f)
{
	if(son[x])
		get1(son[x],x);
	while(n-2*siz[x]<=nowans && nowans)
	{
		ans1[nowans]=x;
		nowans--;
	}
}
void get2(int x,int f)
{
	if(x==root)
		nowans=n,get2(son2,x);
	else
		if(son[x])
			get2(son[x],x);
	while(n-2*siz[x]<=nowans && nowans)
	{
		ans2[nowans]=x;
		nowans--;
	}
}
void get3(int r)
{
	if(son[r])
		get3(son[r]);
	for(int i=head[r],y=ver[i];i;i=Next[i],y=ver[i])
		if(y!=son[r] && d[y]>d[r])
			get3(y);
	int now=son[r]?ans3[son[r]]:r;
	while(siz[now]*2<siz[r])
		now=fa[now];
	ans3[r]=now;
}
void clear()
{
	memset(head,0,sizeof(head));
	memset(Next,0,sizeof(Next));
	memset(ver,0,sizeof(ver));
	memset(son,0,sizeof(son));
	nowans=n,son2=tot=ans=0;
}
bool check1(int x,int y)
{
	return x && siz[son[x]]*2<=siz[y] && (siz[y]-siz[x])*2<=siz[y];
}
bool check2(int x,int y)
{
	if(x==root)
		return siz[son2]*2<=n-siz[y];
	return x && siz[son[x]]*2<=n-siz[y] && (n-siz[y]-siz[x])*2<=n-siz[y];
}
bool check3(int x,int y)
{
	if(x==root)
		return siz[son[x]]*2<=n-siz[y];
	return x && siz[son[x]]*2<=n-siz[y] && (n-siz[y]-siz[x])*2<=n-siz[y];
}
signed main(void){
	cin>>T;
	while(T--){
		cin>>n;
		clear();
		for(register int i=1,x,y;i<n;i++){
			cin>>x>>y;
			add(x,y);
		}
		findroot(1,0);
		memset(son,0,sizeof(son));
		pre(root,0),get1(root,0),get2(root,0),get3(root);
		for(register int i=1;i<=tot;i+=2){
			int x=ver[i],y=ver[ano];
			if(d[x]>d[y])
				swap(x,y);
			int h1=ans3[y];
			ans+=h1;
			if(d[fa[h1]]>=d[y]&&check1(fa[h1],y))
				ans+=fa[h1];
			if(ffa[y]==son[root])
				if(siz[son[root]]-siz[y]>=siz[son2])
					ans+=root;
				else{
					ans+=ans2[siz[y]];
					if(check2(fa[ans2[siz[y]]],y))
						ans+=fa[ans2[siz[y]]];
				}
			else{
				ans+=ans1[siz[y]];
				if(check3(fa[ans1[siz[y]]],y))
					ans+=fa[ans1[siz[y]]];
			}
		}
		cout<<ans;
		puts("");
	}
	cout<<'\n';
	return 0;
}

P4582 [FJOI2014]树的重心

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=200,P=1e4+7;
int n;
vector<int> e[N+7];
int sz[N+7],g[N+7],f[N+7][N+7];
int Dfs1(int u,int fa){
	int res=inf;
	sz[u]=1,g[u]=0;
	for(int&v:e[u])if(v!=fa){
		res=min(res,Dfs1(v,u));
		sz[u]+=sz[v],g[u]=max(g[u],sz[v]);
	}
	g[u]=max(g[u],n-sz[u]);
	res=min(res,g[u]);
	return res;
}
void Dfs2(int u,int fa){
	sz[u]=f[u][0]=f[u][1]=1;
	for(int&v:e[u])if(v!=fa){
		Dfs2(v,u),sz[u]+=sz[v];
		for(int i=sz[u];i>=1;i--)
			for(int j=1;j<=min(sz[v],i-1);j++)
				(f[u][i]+=f[u][i-j]*f[v][j]%P)%=P;			
	}
}
int F1[N+7][N+7],F2[N+7];
int KonnyWen(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) e[i].clear();
	for(int i=1,u,v;i<=n-1;i++){
		scanf("%d%d",&u,&v);
		e[u].pb(v),e[v].pb(u);
	}
	int ms=Dfs1(1,0);
	vector<int> G;
	for(int i=1;i<=n;i++)if(g[i]==ms) G.pb(i);
	memset(f,0,sizeof f),Dfs2(G[0],0);
	int sm=0,res=0; 
	if(sz(G)==1){
		memset(F1,0,sizeof F1),ms=-inf;
		for(int&v:e[G[0]]){
			ms=max(ms,sz[v]),sm+=sz[v];
			for(int i=sm;i>=1;i--)
				for(int j=min(sz[v],i);j>=1;j--){
					if(j==i) (F1[i][j]+=f[v][j])%=P;
					else for(int k=1;k<=min(i,ms);k++)
						(F1[i][max(j,k)]+=F1[i-j][k]*f[v][j]%P)%=P;
				}
		}
		for(int i=1;i<=sm;i++)
			for(int j=1;j<=i;j++)
				if(j*2<=i) (res+=F1[i][j])%=P;
		res++;
	} else if(sz(G)==2){
		memset(F2,0,sizeof F2),F2[0]=1;
		for(int&v:e[G[0]])if(v!=G[1]){
			sm+=sz[v];
			for(int i=sm;i>=1;i--)
				for(int j=1;j<=min(sz[v],i);j++)
					(F2[i]+=F2[i-j]*f[v][j]%P)%=P;
		}
		for(int i=1;i<=sm+1;i++)
			(res+=F2[i-1]*f[G[1]][i]%P)%=P;
	}
	return res;	
}
int main(){
	int t; scanf("%d",&t);
	for(int i=1;i<=t;i++) 
		printf("Case %d: %d\n",i,KonnyWen());
	return 0;
}
posted @ 2022-04-16 10:25  PassName  阅读(1041)  评论(0编辑  收藏  举报