1 3 1 2 1 2 3 2 3 2 3 4
1 1 0HintFor the first query, (2, 3) is the only pair that f(u, v) = 2. For the second query, (1, 3) is the only one. For the third query, there are no pair (u, v) such that f(u, v) = 4.
题意:有一棵n个结点(结点记为0~n),n-1条加权边的树,定义f(u,v)为结点u与结点v之间所有边权值的异或值,即若u与v之间有e1,e2,e3,e4,e5这么五条边的话,f(u,v)=e1⊕e2⊕e3⊕e4⊕e5,现给你一个异或值s,问满足f(u,v)=s的无序对(u,v)有多少对。
放上出题人的解题报告:
首先,我们来证明一下f(u,v)=f(1,u)⊕f(1,v)
假设1到u之间有e6,e7,e8三条边,以及之前假设的u到v的五条边
f(u,v)=e1⊕e2⊕e3⊕e4⊕e5=e1⊕e2⊕e3⊕e4⊕e5⊕(e6⊕e6)⊕(e7⊕e7)⊕(e8⊕e8)=(e6⊕e7⊕e8)⊕(e1⊕e2⊕e3⊕e4⊕e5⊕e6⊕e7⊕e8)=f(1,u)⊕f(1,v)
所以我们只需dfs一遍,算出所有的f(1,u)值,存下每个异或值出现的次数
又因为若a⊕b=c,则a⊕c=b,所以由f(u,v)=f(1,u)⊕f(1,v)=s可得f(1,v)=f(1,u)⊕s
对于每次询问,我们只需加上对于每个结点u,f(1,u)⊕s值出现的次数即可
另外,s=0的情况比较特殊,需另外考虑,因为u==v的情况下,f(u,v)=f(u,u)=0
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<stdio.h> #include<string.h> #include<stdlib.h> #include<queue> #include<math.h> #include<vector> #include<map> #include<set> #include<stdlib.h> #include<cmath> #include<string> #include<algorithm> #include<iostream> #define exp 1e-10 using namespace std; const int N = 100005; const int inf = 1000000000; const int mod = 1000000007; struct node { int to,next,w; }e[2*N]; int h[N],p,s[N]; __int64 m[2*N]; void add_edge(int u,int v,int w) { e[p].to=v; e[p].next=h[u]; e[p].w=w; h[u]=p++; } void dfs(int u,int r) { int v; for(int i=h[u];i+1;i=e[i].next) { v=e[i].to; if(v==r) continue; s[v]=s[u]^e[i].w; m[s[v]]++; dfs(v,u); } } int main() { int t,n,i,a,b,c,q,x,y; __int64 ans,sum; scanf("%d",&t); while(t--) { p=0; memset(h,-1,sizeof(h)); memset(m,0,sizeof(m)); scanf("%d",&n); for(i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); add_edge(a,b,c); add_edge(b,a,c); } s[1]=0; dfs(1,-1);//dfs算出所有的f(1,u),并记录每个异或值出现的次数 scanf("%d",&q); while(q--) { sum=ans=0; scanf("%d",&x); if(!x)//u==v的情况,即s=0 ans+=n; for(i=2;i<=n;i++) { if(x==s[i]) ans++; sum+=m[y=s[i]^x]; if(y==s[i]) sum--; } printf("%I64d\n",sum/2+ans); } } return 0; }菜鸟成长记