让我们异或吧

时间:2021-09-16 11:11:32

洛谷原创的这道题说实话真的不错。。。

题目大意:

求一个树上两个点路径所有边的异或和。

树上求和问题很显然会想到LCA吧,我们假定两个点是u和v,他们的LCA为lca(a,b)

我们维护一个dis数组,dis[i]代表从根节点到i的路径边权的异或和。

我们要求的是两点之间的异或和,我们想从做过的题型中找思路:如果只是简单的求和的话,我们只要用a,b的dis减去两倍lca的dis即可。

但是现在是异或,我们还是从这上去想:我们利用lca来作为中间量。

我们会发现一个数连续异或两次就等于没有异或,并且x^0=x(显然的吧。。。)

所以我们求两点路径间的异或和就可以转化成求两点到根节点的dis的异或和。

所以本题不用lca,只需要遍历一遍树,求出每个点的dis就可以了。

答案就是dis[a]^dis[b].

最后,附上本题代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define maxn 100000
 4 using namespace std;
 5 
 6 int n,head[maxn+5],cnt,m;
 7 int dep[maxn+5],dis[maxn+5];
 8 struct EDGE
 9 {
10     int to,nxt,v;
11 };
12 EDGE edge[maxn*2+5];
13 
14 void add(int x,int y,int z)
15 {
16     edge[++cnt].to=y;
17     edge[cnt].v=z;
18     edge[cnt].nxt=head[x];
19     head[x]=cnt;
20 }
21 void pre_fir(int fa,int u)
22 {
23     for(int i=head[u]; i; i=edge[i].nxt)
24     {
25         if(edge[i].to==fa) continue;
26         dis[edge[i].to]=dis[u]^edge[i].v;
27         pre_fir(u,edge[i].to);
28     }
29 }
30 int main()
31 {
32     scanf("%d",&n);
33     for(int i=1; i<=n-1; i++)
34     {
35         int x,y,z;
36         scanf("%d%d%d",&x,&y,&z);
37         add(x,y,z);
38         add(y,x,z);
39     }
40     scanf("%d",&m);
41     pre_fir(0,1);
42     int a,b;
43     for(int i=1; i<=m; i++)
44     {
45         scanf("%d%d",&a,&b);
46         printf("%d\n",dis[a]^dis[b]);
47     }
48     return 0;
49 }