ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树的重心

2022-04-16 10:31:06  阅读:144  来源: 互联网

标签:head 重心 int siz son fa res


重心

树的重心也叫树的质心。对于一棵树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;
}

标签:head,重心,int,siz,son,fa,res
来源: https://www.cnblogs.com/spaceswalker/p/16152092.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有