1. 程式人生 > 其它 >【P4551 最長異或路徑】題解

【P4551 最長異或路徑】題解

題目連結

題目

給定一棵 \(n\) 個點的帶權樹,結點下標從 \(1\) 開始到 \(n\)。尋找樹中找兩個結點,求最長的異或路徑。

異或路徑指的是指兩個結點之間唯一路徑上的所有邊權的異或

思路

預處理每個點到根節點路勁的異或和,建一棵01trie樹。

對於每個節點,在trie樹上找離它最遠的節點,最後取個 \(\max\) 值即可。

總結

這道題應該也算兩個經典問題的集合吧?

首先求樹上兩個節點之間路徑的異或和,這是一個非常經典的問題,由於重複異或沒影響,所以可以用樹形dp預處理。

這道題巧妙就巧妙在他運用了Trie樹,出現異或,還要求最大,trie樹是一種非常常用的方法,遇到這種情況可以往Trie樹的方面來想。

Code

// Problem: P4551 最長異或路徑
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4551
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 100010
struct node
{
	int x, y, z, n; 
}dx[N*2]; 
struct Node
{
	int s[2]; 
}d[N*30]; 
int n, m, i, j, k; 
int dp[N], u, v, w, h[N]; 
int ans, cnt; 

void cun(int x, int y, int z)
{
	dx[++k].x=x; dx[k].y=y; dx[k].z=z; 
	dx[k].n=h[x]; h[x]=k; 
}

void dfs(int x, int fa)
{
	for(int g=h[x]; g; g=dx[g].n)
	{
		int y=dx[g].y; 
		if(y==fa) continue; 
		dp[y]=dp[x]^dx[g].z; 
		dfs(y, x); 
	}
}

void trie(int x)
{
	int i, j, p=1; 
	for(i=30; i>=0; --i)
	{
		j=(x&(1<<i))>0; 
		if(!d[p].s[j]) d[p].s[j]=++cnt; 
		p=d[p].s[j];
	}
}

int find(int dep, int x, int p)
{
	if(dep<0) return 0; 
	int i=(x&(1<<dep))>0; 
	return find(dep-1, x, (d[p].s[i^1] ? d[p].s[i^1] : d[p].s[i]) )+(d[p].s[i^1] ? (1<<dep) : 0); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	n=read(); 
	for(i=1; i<n; ++i)
	{
		u=read(); v=read(); 
		w=read(); 
		cun(u, v, w); 
		cun(v, u, w); 
	}
	dfs(1, 0); 
	for(cnt=i=1; i<=n; ++i) trie(dp[i]); 
	for(i=1; i<=n; ++i) ans=max(ans, find(30, dp[i], 1)); 
	printf("%d", ans); 
	return 0; 
}

作者: zhangtingxi 轉載請註明出處: https://www.cnblogs.com/zhangtingxi/p/15792154.html