1. 程式人生 > >有根樹同構 (Hash)

有根樹同構 (Hash)

crf 出生的第二秒

1 Description
crf 是一個天才。
他出生的第二秒,往窗外望了一眼,看到了窗外的樹林。
他發現這片樹林非常有趣,因為它只有一排樹,從左到右依次排列。
天才的crf 馬上就把真實的樹的結構抽象成為了圖論中的樹(即任意兩個頂點有且僅有一條路徑可以互相到達的圖)。
crf 不僅頭腦天才,而且他的身體素質也堪稱天才,這也包括了他可以在0.01 秒內用AWP 爆掉隊友的頭的動態視力。在這一秒內他發現非常多的樹其實稍微變一下方向就是長得一樣的。
他想知道,在這片樹林中有多少棵樹和他現在腦中yy 的這棵樹是本質相同的。
兩棵樹本質相同的定義是,對於樹A,存在一種頂點編號的排列,使得樹A 的頂點編號重排後,樹A 的每一條邊的方向和兩個頂點都和樹B 對應相同。
注意,雖然圖論中大多數對樹的定義都是指無根樹,即每條樹邊都是無向邊的樹,但在這個問題中的樹是現實中的樹的抽象,所以我們認為這些樹都是有根樹。
同時為了方便起見,我們假設每棵樹具有相同的結點數。

2 Input
第一行為三個整數n; m; q,表示這排樹林樹的數量、每棵樹的結點數和詢問的數量。
接下來n 部分,每部分有m��1 行,表示一棵樹的m��1 條邊,每行有兩個整數x; y,表示從x 到y 有一條邊。
接下來q 部分,每部分有m-1 行,表示詢問中的一棵樹的m-1 條邊,每行有兩個整數x, y,表示從x 到y 有一條邊。

3 Output
輸出有q 行,每行一個整數,表示該次詢問有多少棵樹和crf 想象的那棵樹本質相同。

4 Sample
Sample Input
1 5 1
1 2
2 3
1 4
4 5
1 3
3 2
1 5
5 4
Sample Output
1

5 Hint
對於30% 的資料,滿足1 <= n, q <= 10; 1 <= m <= 5。
對於100% 的資料,滿足1 <= n m <= 100000; 1 <= q m <= 100000。

思路:
這道題其實是有向圖,題目說的不夠清楚,所以wa掉。。
判結構相同的樹,又強制O1,自然想到hash啦。
考試的時候就寫了一個簡潔的hash,沒想到下來有人說正解不是hash,開始瘋狂的hack。
於是只好被逼寫了一個雙hash,又不停地限制,然後就這樣了。。。
也不知道有沒有問題,反正應該不容易被卡吧。

#include <map>
#include <cstdio> #include <iostream> #include <cstring> #include <cstdlib> #define LL long long #define N 100010 #define base1 7173 #define base2 7177 #define mod1 1000000007 #define mod2 1000000009 #define pr pair<LL, LL> using namespace std; struct Edge{ int to, nxt; }ed[N<<1]; int n, m, q, idc, rt, mxd; int head[N], in[N], siz[N]; LL hsh1[N], hsh2[N], rnd1[N], rnd2[N]; map<pr, int> mp[N]; void inline adde(int u, int v){ ed[++idc].to = v; ed[idc].nxt = head[u]; head[u] = idc; in[v]++; } void inline dfs(int u, int dep){ LL cc1=base2, cc2=base1, cnt=0; siz[u] = 1, mxd = max(mxd, dep); for(register int i=head[u]; i; i=ed[i].nxt){ int v = ed[i].to; dfs(v, dep+1); (hsh1[u] += hsh1[v]) %= mod1; (hsh2[u] += hsh2[v]) %= mod2; (cc1 += rnd1[siz[v]]) %= mod1; (cc2 += rnd2[siz[v]]) %= mod2; siz[u] += siz[v]; cnt++; }//hash:深度, 樹的大小, 子樹大小, 子樹個數 (hsh1[u] += rnd1[siz[u]]) %= mod1; (hsh2[u] += rnd2[siz[u]]) %= mod2; (hsh1[u] += cc1) %= mod1; (hsh2[u] += cc2) %= mod2; (hsh1[u] *= cnt) %= mod1; (hsh2[u] *= cnt) %= mod2; } pr inline solve(){ idc = mxd = 0; for(register int i=1; i<=m; i++) head[i] = in[i] = hsh1[i] = hsh2[i] = 0; for(register int i=1,; i<m; i++){ int u, v; scanf("%d%d", &u, &v); adde(u, v); } for(register int i=1; i<=m; i++) if( !in[i] ){ rt = i; break;}//給出的是合法的樹,所以有唯一一個root dfs(rt, 1); return make_pair(hsh1[rt], hsh2[rt]); } int main(){ freopen ("second.in", "r", stdin); freopen ("second.out", "w", stdout); scanf("%d%d%d", &n, &m, &q); rnd1[0] = rnd2[0] = 1; for(register int i=1; i<=m; i++){ rnd1[i] = rnd1[i-1] * base1 % mod1; rnd2[i] = rnd2[i-1] * base2 % mod2; } for(register int i=1; i<=n; i++){ pr rnd = solve(); mp[mxd][rnd]++; } for(register int i=1; i<=q; i++){ pr rnd = solve(); printf("%d\n", mp[mxd][rnd]); } return 0; }