【树哈希】CF763D Timofey and a flat tree

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/84348781

【题目】
原题地址
给定一棵树,求以哪个点为根时不同构的子树最多。 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

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/84348781