1. 程式人生 > >【樹雜湊】CF763D Timofey and a flat tree

【樹雜湊】CF763D Timofey and a flat tree

【題目】
原題地址
給定一棵樹,求以哪個點為根時不同構的子樹最多。 n 1 0 5 n\leq 10^5

【解題思路】
顯然不同的子樹最多有 2

( n 1 ) 2(n-1) 種,於是我們先維護出以 1 1
為根的所有子樹雜湊值,然後再 d f s dfs 一遍亂搞即可。

這裡的樹雜湊可以選擇給每一種樹分配一個 i d

id 並隨機一個權值,一棵樹的雜湊值可以是它所有子樹的權值和,這樣處理換根時可以十分方便。

如果採用傳統的給子樹排序後再計算雜湊值也可以,只是在第二次 d f s dfs 的時候需要用 b a s bas 的冪來進行計算,比較麻煩。

【參考程式碼】

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

typedef unsigned long long ull;
const int N=2e5+10;
int n,tot,ans,mx,cnt,dif;
int head[N],num[N];
ull val[N],f[N];
map<ull,int>mp;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct Tway{int v,nex;}e[N];
void add(int u,int v)
{
	e[++tot]=(Tway){v,head[u]};head[u]=tot;
	e[++tot]=(Tway){u,head[v]};head[v]=tot;
}

void ins(int x){if(!num[x])++dif;++num[x];}
void del(int x){--num[x];if(!num[x])--dif;}
int get(int x){return mp.count(x)?mp[x]:mp[x]=++cnt;}

void dfs1(int x,int fa)
{
	ull sum=0;
	for(int i=head[x];i;i=e[i].nex)
		if(e[i].v^fa) dfs1(e[i].v,x),sum+=val[f[e[i].v]];
	f[x]=get(sum);ins(f[x]);
}

void dfs2(int x,int fa,int c)
{
	del(f[x]);
	if(dif+1>mx) mx=dif+1,ans=x;

	ull sum=0;
	for(int i=head[x];i;i=e[i].nex)
		if(e[i].v^fa) sum+=val[f[e[i].v]];
	sum+=val[c];
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==fa) continue;
		ull snow=sum-val[f[v]];
		ins(get(snow));dfs2(v,x,get(snow));del(get(snow));
	}
	ins(f[x]);
}

ull rnd(){return (ull)(rand()+1)*2333+(ull)(rand()+1)*19260817+(ull)((rand()+1231)<<28);}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF763D.in","r",stdin);
	freopen("CF763D.out","w",stdout);
#endif
	n=read();
	for(int i=1;i<=n*2;++i) val[i]=rnd();
	for(int i=1;i<n;++i) add(read(),read());
	dfs1(1,0);dfs2(1,0,0);
	printf("%d\n",ans);

	return 0;
}

【總結】
學到了一種新的樹雜湊處理方式qwq