题意
给定一棵树,求以哪个点为根时不同构的子树最多。n≤105n≤100000
题解
树哈希,先求出以1为根时的哈希表
为了换根时哈希表维护是O(1)的 这里采用比较特殊的哈希方式
选择给每一种树分配一个id并随机一个权值,一棵树的哈希值是它所有子树的权值和
画图手推例子会比较好理解 要理解好 f数组和val数组
f数组其实是子树sum值最早出现的顺序 val[f[x]]为子树新哈希值 为了方便计数用f[x]代替子树哈希值
采用传统的给子树排序后再计算哈希值会比较麻烦 时间复杂度也会出问题
1 #include<cstdio> 2 #include<cstdlib> 3 #include<map> 4 using namespace std; 5 typedef unsigned long long ull; 6 const int N=100000+5; 7 8 int n,dif,ans,mx; 9 10 int f[N]; ull val[2*N]; 11 12 int tot; map<ull,int > sta; 13 14 int num,last[N],nxt[2*N],ver[2*N]; 15 inline void add(int x,int y) {nxt[++num]=last[x]; last[x]=num; ver[num]=y;} 16 17 int cnt[2*N]; 18 inline void ins(int x) {if(!cnt[x]) dif++; ++cnt[x];} 19 inline void del(int x) {cnt[x]--; if(!cnt[x]) dif--;} 20 inline int get(int x) {return sta.count(x)?sta[x]:sta[x]=++tot;} // 求排名 21 22 void pre(int x,int fa) 23 { ull sum=0; 24 for(int i=last[x];i;i=nxt[i]) 25 if(ver[i]!=fa) {pre(ver[i],x); sum+=val[f[ver[i]]]; } 26 f[x]=get(sum); ins(f[x]); 27 } 28 29 void dfs(int x,int fa,int c) 30 { del(f[x]); // +1 是x为根的树哈希值显然没有与它相同的 31 if(dif+1>mx) mx=dif+1,ans=x; 32 else if(dif+1==mx && x<ans) ans=x; 33 34 ull sum=0; 35 for(int i=last[x];i;i=nxt[i]) 36 if(ver[i]!=fa) sum+=val[f[ver[i]]]; 37 sum+=val[c]; 38 39 for(int i=last[x];i;i=nxt[i]) 40 {int y=ver[i]; 41 if(y==fa) continue; 42 ull now=sum-val[f[y]]; // 换根之后 维护x子树哈希值 43 44 ins(get(now)); dfs(y,x,get(now)); del(get(now)); 45 } 46 ins(f[x]); 47 } 48 inline ull rnd() {return (ull)(rand()+1)*2333+(ull)(rand()+1)*19260817+(ull)((rand()+1231)<<28);} 49 int main() 50 { 51 scanf("%d",&n); int x,y; 52 for(int i=1;i<n;i++) {scanf("%d%d",&x,&y); add(x,y); add(y,x);} 53 54 for(int i=1;i<=2*n;i++) val[i]=rnd(); 55 56 pre(1,0); dfs(1,0,0); 57 printf("%d",ans); 58 return 0; 59 }