[題目記錄]一本通高手訓練-獨特的樹葉

youlv發表於2024-12-03

題意

給出 \(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;
}


相關文章