Description
给定一棵有n个节点的树(编号为1, 2, 3, ..., n),树上每条边都有一个权值。现在有m个询问,每个询问给定u,v,对于每个询问,输出 e1 xor e2 xor e3 ... (ei 为u到v路径上的边的权值) 的值。
(xor 为二进制中的异或运算, 0 ^ 0 = 0, 1 ^ 1 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1)
Input
第一行有一个整数T,代表数据组数。 (T <= 20)
接下来有T组数据,每组数据第一行有两个整数n,m。(2 <= n <= 40000, 1 <= m <= 100000)。
接下来的 n - 1 行,每行有三个整数u, v, w,代表 u 和 v 之间有一条权值为 w 的边。(0 <= w <= 1e9)。
接下来的 m 行,每行有两个整数u, v,代表每个询问的两个点。
Output
对于每组数据,
第一行输出 “Case x:”, 其中x为第几组数据。
接下来的m行,每行输出一个整数,代表每个询问的答案。
Sample Input
3 3
1 2 3
1 3 4
1 2
1 3
2 3
Sample Output
3
4
7
题解来自校ACM群:
题意就是求树链上的所有边权的异或(XOR)。
这里首先要说明XOR的一些性质。
首先XOR满足交换率、结合律。
然后又有A xor A =0 , A xor 0 = A
所以就有一个很有用的特性:(A xor B) ^ (B xor C) = A xor C
因此,如果我们把这棵树的任意点(比如1)作为根,预处理出树根到所有点的路径的异或值F[i],那么对于每一次查询u, v,答案就是F[u] xor F[v]。
为什么呢? 看看如下这张图,这两个值异或之后,就正好把从根到两条链的交点处的异或值算了两遍,考虑异或的性质就会发现,正好就可以把值抵消掉。
时间复杂度O(N+Q)
我用vector实现链表建树
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int MAXN=10005;
int F[MAXN];
vector< pair<int,int> > G[MAXN];
void dfs(int cur,int par)
{
for(int i=0;i<G[cur].size();i++)
{
int v=G[cur][i].first;
int d=G[cur][i].second;
if(v==par) continue;
F[v]=F[cur]^d;
dfs(v,cur);
}
}
int main(void)
{
int t;
int cas=1;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
G[i].clear();
for(int i=1;i<=n-1;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
memset(F,0,sizeof(F));
F[1]=1;
dfs(1,0);
printf("Case %d:\n",cas++);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",F[a]^F[b]);
}
}
return 0;
}