【树哈希】CodeForces 763D Timofey and a flat tree

题意

   给定一棵树,求以哪个点为根时不同构的子树最多。n105n100000

题解

  树哈希,先求出以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  }

 

猜你喜欢

转载自www.cnblogs.com/YuXiaoze/p/11291214.html