洛谷原创的这道题说实话真的不错。。。
题目大意:
求一个树上两个点路径所有边的异或和。
树上求和问题很显然会想到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 }