Xor Path
时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld
题目描述
给定一棵n个点的树,每个点有权值A i。定义path(i,j)表示i 到j 的最短路径上,所有点的点权异或和。
对于i=1∼n−1, j=i+1∼n,求所有path(i,j)的异或和。
输入描述:
第一行一个整数n。 接下来n-1行,每行2个整数u,v,表示u,v之间有一条边。 第n+1行有n个整数,表示每个点的权值A i。
输出描述:
输出一个整数,表示所有{\mathbb{path}(i,j)}path(i,j)的异或和,其中 i=1∼n−1, j=i+1∼n。
示例1
输入
复制
4 1 2 1 3 1 4 1 2 3 4
输出
复制
5
题解:
根据异或的性质,一个数异或两次就没了,所以一个点异或两次(偶数次)就相当于没有。所以我们只需要统计每个点被异或了多少次
我们考虑一下一个点x会有多少种情况:
size[x]表示以x为根的子树内有多少点
- x作为一个路径的顶点 ,那么就要与剩下的点直接连接,所以就是n-1
- 当x为根时,x的两个子树(其中一个子树名为y)中任意点进行连接。那么就是他子树的节点个数两两相乘。子树所有点为size[x]-1,子树y:size[y],剩余点为size[x]-1-size[y],最后两个相乘size[y]×(n−1−size[y]) y是动态取值,
- 当x不为根时, x的子树与x的父亲节点的子树(除了x之外的)之间的路径经过该点的次数。子树点个数为size[x]-1,非子树个数为n-size[x],总数就是(size[x]−1)×(n−size[x])
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int size[maxn]; int a[maxn],sum; vector<int>g[maxn]; typedef long long ll; int n; void dfs(int u,int fa) { size[u]=1; ll res=0;//记录奇偶次数 for(auto x:g[u]) { if(x==fa)continue; dfs(x,u); size[u]+=size[x];//更新以u为根的树的大小 res+=1ll*(size[u]-size[x]-1)*size[x];//我们只需要知道奇偶性即可 } res+=1ll*(size[u]-1)*(n-size[u]);//第三种情况 // res>>=1;//多计算了一遍 res+=n-1; //第一种情况 if(res&1)//如果为奇数记入结果 sum^=a[u]; } int main() { cin>>n; for(int i=1;i<n;i++) { int u,v; cin>>u>>v; g[u].push_back(v); g[v].push_back(u); } for(int i=1;i<=n;i++) { cin>>a[i];//点值 } dfs(1,0);//从第一个点开始 cout<<sum<<endl; return 0; }
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int size[maxn]; int a[maxn],sum; vector<int>g[maxn]; typedef long long ll; int n; void dfs(int u,int fa) { size[u]=1; ll res=0;//记录奇偶次数 for(auto x:g[u]) { if(x==fa)continue; dfs(x,u); res+=1ll*(n-size[x]-1)*size[x];//我们只需要知道奇偶性即可 size[u]+=size[x];//更新以u为根的树的大小 } res+=1ll*(size[u]-1)*(n-size[u]);//第一和第三种情况 res>>=1;//多计算了一遍 res+=n-1; if(res&1)//如果为奇数记入结果 sum^=a[u]; } int main() { cin>>n; for(int i=1;i<n;i++) { int u,v; cin>>u>>v; g[u].push_back(v); g[v].push_back(u); } for(int i=1;i<=n;i++) { cin>>a[i];//点值 } dfs(1,0);//从第一个点开始 cout<<sum<<endl; return 0; }