最长异或路径
时间限制: 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)=⊕e∈pw(e)
⊕ ⊕ ⊕是异或符号。
我们认为一条路径是最长异或路径当且经当它拥有最长的异或长度。给定一棵有边权的树,你能找出最长异或路径吗?输入格式
输入包括若干组测试数据。每个测试点的第一行包括一个整数 n n n ( 1 ≤ n ≤ 100000 ) (1≤n≤100000) (1≤n≤100000),接下来 n − 1 n-1 n−1行每行包括3个整数 u ( 0 ≤ u < n ) , v ( 0 ≤ v < n ) , w ( 0 ≤ w < 2 31 ) u(0 ≤ u < n),v(0 ≤ v < n),w(0 ≤ w < 2^{31}) u(0≤u<n),v(0≤v<n),w(0≤w<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 − > 1 − > 2 0->1->2 0−>1−>2,长度是 7 ( = 3 ⊕ 4 ) 7 (=3 ⊕ 4) 7(=3⊕4)
以上翻译由作者原创,不喜勿喷,转载请注明出处
解析
首先对于静态的树上两点间异或路径,我们可以通过树上前缀和解决。
任取一点为根,统计出根到每个点
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,那么无论如何都比该位 0 0 0最终的答案更大
- 当两个数在二进制下某一位不同时,异或后的答案才为
1
1
1
因此我们可以通过 T r i e Trie Trie树来实现这个操作。
具体实现:
- 对于每个 d [ i ] d[i] d[i]将其转化成二进制数后从前往后插入 T r i e Trie Trie树中。
- 对于每个 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;
}