POJ3764(树上前缀和+Trie树)

6 篇文章 0 订阅
1 篇文章 0 订阅

最长异或路径

时间限制: 2000 M S 2000MS 2000MS 空间限制: 64 M B 64MB 64MB

题目描述

在一棵有边权的树上,一条路径 p p p的异或长度定义为 p p p上所有边权的异或和
x o r l e n g t h ( p ) = ⊕ e ∈ p w ( e ) _{xor}length(p)=\oplus_{e \in p}^w(e) xorlength(p)=epw(e)
⊕ ⊕ 是异或符号。
我们认为一条路径是最长异或路径当且经当它拥有最长的异或长度。给定一棵有边权的树,你能找出最长异或路径吗?

输入格式

输入包括若干组测试数据。每个测试点的第一行包括一个整数 n n n ( 1 ≤ n ≤ 100000 ) (1≤n≤100000) (1n100000),接下来 n − 1 n-1 n1行每行包括3个整数 u ( 0 ≤ u &lt; n ) , v ( 0 ≤ v &lt; n ) , w ( 0 ≤ w &lt; 2 31 ) u(0 ≤ u &lt; n),v(0 ≤ v &lt; n),w(0 ≤ w &lt; 2^{31}) u(0u<n),v(0v<n),w(0w<231)表示在节点 u u u v v v之间有一条长为 w w w的边。

输出格式

对于每组测试数据输出最长异或路径的异或长度。
样例输入
4 4 4
0 0 0 1 1 1 3 3 3
1 1 1 2 2 2 4 4 4
1 1 1 3 3 3 6 6 6

样例输出

7 7 7

提示

最长异或路径是 0 − &gt; 1 − &gt; 2 0-&gt;1-&gt;2 0>1>2,长度是 7 ( = 3 ⊕ 4 ) 7 (=3 ⊕ 4) 7(=34)
以上翻译由作者原创,不喜勿喷,转载请注明出处


解析

首先对于静态的树上两点间异或路径,我们可以通过树上前缀和解决。
任取一点为根,统计出根到每个点 u u u路径上的异或长度 d [ u ] d[u] d[u]
那么对于任意两点 u , v u,v u,v之间的异或路径就 = d [ u ] =d[u] =d[u] x o r xor xor d [ v ] d[v] d[v] x o r xor xor d [ l c a ( u , v ) ] d[lca(u,v)] d[lca(u,v)] x o r xor xor d [ l c a ( u , v ) ] d[lca(u,v)] d[lca(u,v)] = d [ u ] =d[u] =d[u] x o r xor xor d [ v ] d[v] d[v] ( x (x (x x o r xor xor x = 0 ) x = 0) x=0)


于是,问题就变成了在 n n n个数中取出两个数使得它们的异或值最大。
如果直接 O ( n 2 ) O(n^2) O(n2)暴力枚举显然是会超时的。
我们考虑二进制下每一位对答案的影响:

  1. 如果该位可以为 1 1 1,那么无论如何都比该位 0 0 0最终的答案更大
  2. 当两个数在二进制下某一位不同时,异或后的答案才为 1 1 1
    因此我们可以通过 T r i e Trie Trie树来实现这个操作。
具体实现:
  1. 对于每个 d [ i ] d[i] d[i]将其转化成二进制数后从前往后插入 T r i e Trie Trie树中。
  2. 对于每个 d [ i ] d[i] d[i]去已经生成好的 T r i e Trie Trie树中二分,若当前节点有与当前位不同的儿子节点,则往该儿子节点搜索,否则只能往与当前位相同的儿子节点搜索。

T i p s : Tips: Tips:因为有多组数据,注意将 T r i e Trie Trie树手动清空。


代码

#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100005;
const int maxe = 100005;
int edgenum;
int Next[maxe << 1] , vet[maxe << 1] , val[maxe << 1] , head[maxn];
int d[maxn];
int len;
bool a[35];
int id;
int max(int x , int y){return x > y ? x : y;}
void swap(int &x , int &y){x ^= y , y ^= x , x ^= y;}
int read()
{
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	int res = 0;
	while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48) , ch = getchar();
	return res;
}
void add_edge(int u , int v , int cost)
{
	edgenum++;
	Next[edgenum] = head[u];
	vet[edgenum] = v;
	val[edgenum] = cost;
	head[u] = edgenum;
}
void dfs(int u , int fa)
{
	for(int e = head[u];e;e = Next[e])
	{
		int v = vet[e];
		if(v == fa) continue;
		d[v] = d[u] ^ val[e];
		dfs(v , u);
	}
}
struct Trie
{
	private:
		int cnt;
		int ch[35 * maxn][2] , d_id[35 * maxn];
	public:
		int rt;
		void clear()
		{
			rt = 1 , cnt = 1;
			memset(ch , 0 , sizeof ch);
		}
		void insert(int &u , int loc)
		{
			if(!u) u = ++cnt;
			if(loc > len) {d_id[u] = id; return;}
			insert(ch[u][a[loc]] , loc + 1);
		}
		int query(int u , int loc)
		{
			if(loc > len) return d_id[u];
			if(ch[u][a[loc] ^ 1]) return query(ch[u][a[loc] ^ 1] , loc + 1);
			return query(ch[u][a[loc]] , loc + 1);
		}
}trie;
void change(int x)
{
	len = 32;
	for(int i = 1;i <= len;i++) a[i] = x >> len - i & 1;
}
int main()
{
	int n;
	while(scanf("%d",&n) == 1)
	{
		edgenum = 0;
		for(int i = 1;i <= n;i++) head[i] = 0;
		for(int i = 1;i < n;i++)
		{
			int u = read() + 1 , v = read() + 1 , cost = read();
			add_edge(u , v , cost);
			add_edge(v , u , cost);
		}
		for(int i = 1;i <= n;i++) d[i] = 0;
		dfs(1 , 0);
		trie.clear();
		for(int i = 1;i <= n;i++) change(d[i]) , id = i , trie.insert(trie.rt , 1);
		int ans = 0;
		for(int i = 1;i <= n;i++) change(d[i]) , ans = max(ans , d[trie.query(trie.rt , 1)] ^ d[i]);
		printf("%d\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值