日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【BZOJ - 4754】独特的树叶(树哈希)

發布時間:2023/12/10 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【BZOJ - 4754】独特的树叶(树哈希) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題干:

JYY有兩棵樹A和B:樹A有N個點,編號為1到N;樹B有N+1個點,編號為1到N+1。JYY知道樹B恰好是由樹A加上一個葉

節點,然后將節點的編號打亂后得到的。他想知道,這個多余的葉子到底是樹B中的哪一個葉節點呢?

?

Input

輸入一行包含一個正整數N。

接下來N-1行,描述樹A,每行包含兩個整數表示樹A中的一條邊;

接下來N行,描述樹B,每行包含兩個整數表示樹B中的一條邊。

1≤N≤10^5

?

Output

輸出一行一個整數,表示樹B中相比樹A多余的那個葉子的編號。如果有多個符合要求的葉子,輸出B中編號最小的

那一個的編號

?

Sample Input

5 1 2 2 3 1 4 1 5 1 2 2 3 3 4 4 5 3 6

Sample Output

1

Hint

解題報告:?

哈希規則:子樹u的哈希值由它的每一個子樹vi的哈希值得來,首先將所有f(v)排個序(防止順序不同造成影響),然后

W是事先選取的一個位權,MOD是模數,size(u)是子樹u的大小。

這樣DFS一遍可求出以1號節點為根時,所有子樹的哈希值f(u)。

但是這是無根樹,我們想求出以任意節點為根時整棵樹的哈希值。

設fa[u]以1為根時u的父親,則上面的f(u)也是以fa[u]為根時子樹u的哈希值。

再求一個g(u)表示以u為根時子樹fa[u]的哈希值。這個g(u)怎么求呢?再DFS一遍,對于每個節點,g(u)由g(fa[u])以及u的每個兄弟vi的f(vi)得來。但是直接暴力枚舉的話在菊花圖上是O(n^2)的,那怎么辦呢?

對于每個節點u維護一個數組,存儲它所有兒子的哈希值f(v),如果有父親,則g(u)也在里面,把這個數組排好序,求出每個前綴的哈希值和每個后綴的哈希值。這時,以u為根時整棵樹的哈希值就是整個數組的哈希值(再乘上子樹大小n)。

此時求每個兒子v的g(v),就是從那個數組中間去掉f(v)后的哈希值,二分查找后把前綴哈希值和后綴哈希值拼起來就可以得到。記得乘上v為根時uu的size即n?size(v)。

這樣就求出以每個節點為根的哈希值了。

把A的所有哈希值存到一個set里,然后枚舉B的每個度為1的點u,求出以u為根它的唯一子樹v的哈希值,如果set里有這個值,u就是所求的點之一。部分內容參考自:鏈接

首先明確一個問題,以u為根節點的樹的Hash值=樹的大小 * 子樹Hash值的帶權和 % MOD,也就是說這個根節點是以子樹大小的身份參與到其中的。也就是HASH[u]其實重頭戲在于u的孩子節點,而u在其中的作用只是size那一部分權重而已,其他的都與他無關。這是在做樹的問題的時候,和其他問題不一樣的一點,也是比較有特色的一點。?

g[v]代表以v為根時,子樹u的Hash值。(也就是以v為根節點的 部分哈希和)那么g[v]由g[u]和v的一系列兄弟節點f[vi]帶權得來。

AC代碼:

#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 2e5 + 5; const ll MOD = 2000020331; //const ll MOD = 2000004199; 用這個模數都沒有問題! const ll seed = 31;// ll PW[MAX]; struct Edge {int v,ne; } e[MAX]; int head[MAX],tot; int n; int deg[MAX]; bool isB; void add(int u,int v) {if(isB) deg[u]++;e[++tot].v = v;e[tot].ne = head[u]; head[u] = tot; } int size[MAX],fa[MAX]; vector<ll> son[MAX],sl[MAX],sr[MAX]; ll f[MAX],g[MAX]; set<ll> vis; ll dfs1(int cur,int rt) {ll res = 0;fa[cur] = rt; son[cur].clear();//必須要清空!! size[cur] = 1;for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].v; if(v == rt) continue;ll tmp = dfs1(v,cur);son[cur].push_back(tmp);size[cur] += size[v]; }if(son[cur].empty()) return f[cur] = 1;//注意葉子結點的HASH值需要是1,而不能是0,也就是son數組中必須都是正數的HASH值。sort(son[cur].begin(),son[cur].end());int up = son[cur].size();for(int i = 0; i<up; i++) res = (res * seed + son[cur][i]) % MOD; return f[cur] = size[cur] * res % MOD; } int ans; void dfs2(int u) {if(fa[u]) {son[u].pb(g[u]);sort(son[u].begin(),son[u].end());}int up = son[u].size();sl[u].resize(up);sl[u][0] = son[u][0]; for(int i = 1; i<up; i++) {//好像不太能把初始化合并進來 sl[u][i] = (sl[u][i-1] * seed + son[u][i])%MOD;}sr[u].resize(up);sr[u][up-1] = son[u][up-1];for(int i = up-2; i>=0; i--) sr[u][i] = (sr[u][i+1] + son[u][i] * PW[up-i-1])%MOD; //其實不是求后綴的HASH值,而是前綴HASH的后綴和,所以要這么寫 for(int i = head[u]; ~i; i = e[i].ne) {int v = e[i].v;if(v == fa[u]) continue;if(up == 1) {g[v] = 1; dfs2(v); break;}int p = lower_bound(son[u].begin(),son[u].end(),f[v]) - son[u].begin();g[v] = 0;if(p+1 < up) g[v] = sr[u][p+1];if(p-1 >= 0) g[v] = (g[v]+sl[u][p-1]*PW[up-1-p])%MOD;g[v] = g[v] * (n-size[v]) % MOD;if(isB && deg[v] == 1 && vis.find(g[v]) != vis.end()) ans = min(ans, v);dfs2(v);}if(!isB) vis.insert(sl[u][up - 1] * n % MOD); } int main() {PW[0] = 1;for(int i = 1; i<MAX; i++) PW[i] = PW[i-1]*seed % MOD;cin>>n;memset(head,-1,sizeof head);for(int u,v,i = 1; i<n; i++) {cin>>u>>v;add(u,v);add(v,u);}dfs1(1,0); dfs2(1);tot=0,isB=1,n++;memset(head,-1,sizeof head);for(int u,v,i = 1; i<n; i++) {cin>>u>>v;add(u,v);add(v,u);} dfs1(1,0);ans=1e9;if(deg[1] == 1 && vis.find(f[e[head[1]].v]) != vis.end()) ans = 1;dfs2(1);printf("%d\n",ans); return 0 ; }

另一種及其簡單的Hash方式:

我們只需要就可以了。

其中是每一個子樹的hash值
這個函數和上一個函數一樣,支持換根,那么就不限于找重心了
那第一顆樹所有的hash值丟進set中,第二顆樹刪除一個節點的hash值也可以用類似的方法弄出來。

#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 2e5 + 5; //const ll MOD = 2000020331;//這個模數在這個方法中可以直接扔掉了,因為根本不是質數所以肯定要不了,因為逆元的求法就不對。 const ll MOD = 2000004199; //用這個模數都沒有問題! const ll seed = 13331;//可換! 233,2333,23333對于第二個模數都可以過 ll INV; struct Edge {int v,ne; } e[MAX]; int head[MAX],tot; int n; void adde(int u,int v) {e[++tot].v = v;e[tot].ne = head[u]; head[u] = tot; } inline ll qpow(ll a,ll b) {ll res = 1;while(b) {if(b&1) res = res * a % MOD;a = a *a % MOD;b >>= 1; }return res; } inline ll add(ll x,ll y) {return (x+y)%MOD;} inline ll mul(ll x,ll y) {return (x*y)%MOD;} inline ll sub(ll x,ll y) {return (x-y+MOD)%MOD;} ll H[MAX]; int size[MAX],du[MAX]; void dfs(int cur,int fa) {H[cur] = 1;size[cur] = 1;for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].v;if(v == fa) continue;dfs(v,cur);size[cur] = add(size[cur],size[v]);H[cur] = mul(H[cur],H[v]);}H[cur] = add(H[cur],size[cur]);H[cur] = mul(H[cur],seed); } set<ll> ss; bool isB; void dfs1(int cur,int fa) {if(isB==0) ss.insert(H[cur]);//必須要先插入,因為根的情況。 ll Hall = mul(H[cur],INV),Hres;Hall = sub(Hall,n);for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].v;if(v == fa) continue;Hres=mul(Hall,qpow(H[v],MOD-2));Hres=add(Hres,n-size[v]); Hres=mul(Hres,seed);//至此Hres代表除v的樹的Hash值H[v]=mul(H[v],INV); H[v]=sub(H[v],size[v]);H[v]=mul(H[v],Hres);H[v]=add(H[v],n);H[v]=mul(H[v],seed);dfs1(v,cur);} } int main() {INV = qpow(seed,MOD-2);cin>>n;tot=0;memset(head,-1,sizeof head);for(int u,v,i = 1; i<n; i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);dfs(1,0);dfs1(1,0);tot=0;memset(head,-1,sizeof head);n++;for(int u,v,i = 1; i<n; i++) {scanf("%d%d",&u,&v);du[u]++,du[v]++;adde(u,v);adde(v,u);}isB=1;dfs(1,0);dfs1(1,0);for(int i = 1; i<=n; i++) {if(du[i] == 1) {ll Hres = mul(H[i],INV);Hres = sub(Hres,n);if(ss.find(Hres) != ss.end()) {printf("%d\n",i);return 0 ;}}}return 0 ; }

?

總結

以上是生活随笔為你收集整理的【BZOJ - 4754】独特的树叶(树哈希)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。