1. 程式人生 > 其它 >【校招VIP】產品分析考點之資料指標提高

【校招VIP】產品分析考點之資料指標提高

重心

樹的重心也叫樹的質心。對於一棵樹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;
}