題意
給出 \(n\) 個節點的樹 \(T\) , 和 \(n+1\) 個節點的樹 \(T'\) .其中 \(T'\) 是將 \(T\) 的序號打亂 , 再加上一個葉子節點構成的 . 求新加入的節點在 \(T'\)上的編號 .
\(n=1e5\)
(bzoj4754)
題解
樹雜湊 可以用來確定一棵無編號有根樹 . 一種寫法是
\[h_u=k , k是常數 , u是葉子
\\h_u=k+\sum_v\mathrm{shift}(h_v)\ ,u不是葉子
\]
其中 shift 操作是形如 x=x^(x<<7)
的異或 , 移位操作 , 意在打亂和雜湊 , 保證正確性 .
這樣寫同樣保證了在根節點位置的雜湊值相當於兒子的和雜湊 , 便於進行換根之類的操作 .
回到本題 , 使用樹雜湊記錄無根樹 \(T\) , 可以把每個節點作為根都扔到 map
裡 . 具體實現就是換根dp.
對於樹 \(T'\) , 同理地 , 計算雜湊值 , 在葉子節點處用雜湊值在 map
裡查詢即可.
點選檢視程式碼
#include<bits/stdc++.h>
#define file(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define ll long long
#define ull unsigned long long
#define INF 0x7fffffff
#define INF64 1e18
using namespace std;
constexpr int N=1e5+5;
ull h[N],mask=1145141919810;
ull shift(ull x){
x^=mask;
x^=x<<13;
x^=x>>11;
x^=x<<7;
x^=mask;
return x;
}
vector<int> e[N];
int n;
void dfs1(int u,int fa){
h[u]=1;
for(auto v:e[u]){
if(v==fa) continue;
dfs1(v,u);
h[u]+=shift(h[v]);
}
}
map<ull,int> mp;
void dfs2(int u,int fa){
mp[h[u]]=1;
for(auto v:e[u]){
if(v==fa) continue;
h[u]-=shift(h[v]);
h[v]+=shift(h[u]);
dfs2(v,u);
h[v]-=shift(h[u]);
h[u]+=shift(h[v]);
}
}
int res;
void dfs3(int u,int fa){
for(auto v:e[u]){
if(e[v].size()==1){
if(mp.count(h[u]-shift(h[v]))) res=min(res,v);
}
if(v==fa) continue;
h[u]-=shift(h[v]);
h[v]+=shift(h[u]);
dfs3(v,u);
h[v]-=shift(h[u]);
h[u]+=shift(h[v]);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,1);
dfs2(1,1);
for(int i=1;i<=n;i++) e[i].clear();
for(int i=1;i<=n;i++){
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,1);
res=INF;
dfs3(1,1);
cout<<res;
}