有根樹同構 (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;
}