1. 程式人生 > >4337. [BJOI2015]樹的同構【樹哈希】

4337. [BJOI2015]樹的同構【樹哈希】

樹同構 print pac 一行 編號 cstring return NPU ace

Description

樹是一種很常見的數據結構。 我們把N個點,N-1條邊的連通無向圖稱為樹。 若將某個點作為根,從根開始遍歷,則其它的點都有一個前驅,這個樹就成為有根樹。 對於兩個樹T1和T2,如果能夠把樹T1的所有點重新標號,使得樹T1和樹T2完全相 同,那麽這兩個樹是同構的。也就是說,它們具有相同的形態。 現在,給你M個有根樹,請你把它們按同構關系分成若幹個等價類。

Input

第一行,一個整數M。 接下來M行,每行包含若幹個整數,表示一個樹。第一個整數N表示點數。接下來N 個整數,依次表示編號為1到N的每個點的父親結點的編號。根節點父親結點編號為0。

Output

輸出M行,每行一個整數,表示與每個樹同構的樹的最小編號。

Sample Input

4
4 0 1 1 2
4 2 0 2 3
4 0 1 1 1
4 0 1 2 3

Sample Output

1
1
3
1

HINT

【樣例解釋】 編號為1, 2, 4 的樹是同構的。編號為3 的樹只與它自身同構。 100% 的數據中,1 ≤ N, M ≤ 50。 樹hash,x的子樹計算方法為$\sum hash[i]*val[i]$,其中hash[i]表示的是x的兒子的第i大的哈希值,val[i]是隨機的一組很大的數。
 1
#include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<map> 5 #include<algorithm> 6 #define LL long long 7 #define MOD (998244353) 8 using namespace std; 9 10 struct Edge{int to,next;}edge[201]; 11 LL T,n,x,ans,hash[101],val[101]; 12 int head[101
],num_edge; 13 map<LL,LL>Map; 14 15 void add(int u,int v) 16 { 17 edge[++num_edge].to=v; 18 edge[num_edge].next=head[u]; 19 head[u]=num_edge; 20 } 21 22 void Dfs(int x,int fa) 23 { 24 LL q[101],tot=0; 25 hash[x]=0; 26 for (int i=head[x]; i; i=edge[i].next) 27 if (edge[i].to!=fa) 28 Dfs(edge[i].to,x),q[++tot]=hash[edge[i].to]; 29 if (tot==0){hash[x]=1; return;} 30 sort(q+1,q+tot+1); 31 for (int i=1; i<=tot; ++i) 32 hash[x]=(hash[x]+q[i]*val[i])%MOD; 33 } 34 35 int main() 36 { 37 for (int i=1; i<=50; ++i) 38 val[i]=rand()*233473ll+rand()*19260817ll+rand(); 39 scanf("%d",&T); 40 for (int t=1; t<=T; ++t) 41 { 42 scanf("%d",&n); 43 memset(head,0,sizeof(head)); num_edge=0; 44 for (int i=1; i<=n; ++i) 45 { 46 scanf("%d",&x); 47 if (!x) continue; 48 add(x,i), add(i,x); 49 } 50 ans=233; 51 for (int i=1; i<=n; ++i) 52 { 53 Dfs(i,-1); 54 if (!Map[hash[i]]) Map[hash[i]]=t; 55 else Map[hash[i]]=min(Map[hash[i]],(LL)t); 56 ans=min(ans,Map[hash[i]]); 57 } 58 printf("%lld\n",ans); 59 } 60 }

4337. [BJOI2015]樹的同構【樹哈希】