ABC201E Xor Distances 题解
题目大意
给定一个带权树,求树上每两点的简单路径上的边权的异或和的和。
形式化的,定义 \(dis(i,j)\) 为 \(i\) 到 \(j\) 的简单路径上的边权的异或和,求 \(\large\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\text{dis}(i,j)\)。
Solve
令 \(\large f(u)=\sum\limits_{i=1}^n\text{dis}(u,i)\)。
指定 \(1\) 为根,考虑先 dfs 遍历树求出 \(f(1)\),然后换根 \(\text{DP}\)。
若已知 \(f(u)\),对于 \(v\in son_u\),我们分两部分考虑,即在子树 \(v\) 中的 \(A\) 集合和不在子树 \(v\) 中的 \(B\) 集合。
- 对于 \(B\) 中的点,根从 \(u\) 转移到 \(v\),他们到根的路径的异或和会多异或上 \(w(u,v)\)。
- 对于 \(A\) 中的店,他们到根的路径的异或和会少异或上 \(w(u,v)\),由于异或的自反性,通过再异或上一个 \(w(u,v)\) 也可以实现。
综上,\(f(v)\) 即为 \(f(u)\) 的每一项异或上 \(w(u,v)\) 的和,即 \(\large f(v)=\sum\limits_{i=1}^n dis(u,i)\oplus w(u,v)\)。
考虑如何计算。
不难想到:用 \(cnt\) 记录下 \(f(u)\) 的每一项 \(x\) 二进制下 \(1\) 和 \(0\) 的个数,按位枚举 \(w(u,v)\),若其第 \(i\) 位为 \(1\),则 \(x\oplus w(u,v)\) 后第 \(i\) 位会与原来相反,所以此时交换 \(cnt_{i,1}\) 和 \(cnt_{i,0}\)。
经过交换后,我们有 \(\large f(v)=\sum\limits_{i=0}^{m-1}2^i\times cnt_{i,1}\)。本题 \(w\leq2^{60}\),故 \(m=60\)。
答案即为 \(\large\frac {\sum\limits_{u=1}^nf(u)} 2\)。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{short f=1;int x=0;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();return x*f;
}
#define mod 1000000007
int n,cnt[60][2],ans,mi[60]={1};
#define PII pair<int,int>
vector<PII>e[200010];
void dfs1(int u,int d,int fa)
{for(int i=59;i>=0;i--) cnt[i][d>>i&1]++;for(auto i:e[u])if(i.first!=fa) dfs1(i.first,d^i.second,u);
}
void dfs2(int u,int fa)
{for(int i=59;i>=0;i--) ans=(ans+mi[i]*cnt[i][1]%mod)%mod;for(auto i:e[u])if(i.first!=fa){for(int j=59;j>=0;j--)if(i.second>>j&1)swap(cnt[j][1],cnt[j][0]);dfs2(i.first,u);for(int j=59;j>=0;j--)if(i.second>>j&1)swap(cnt[j][1],cnt[j][0]);}
}
signed main()
{n=read();for(int i=1;i<60;i=-~i) mi[i]=(mi[i-1]<<1)%mod;for(int i=1,u,v,w;i<n;i=-~i)u=read(),v=read(),w=read(),e[u].push_back({v,w}),e[v].push_back({u,w});dfs1(1,0,0);dfs2(1,0);return printf("%lld",ans*500000004/*显然这是2在mod 1e9+7下的逆元*/%mod),0;
}