https://nanti.jisuanke.com/t/39277
题意
给出一棵树,以及树边权,求出这个公式
题意
这里规定了偏序关系,所以不用重复计算两次。
将这题转化为求两个点所做的贡献。
首先要明白异或的性质。
a ^ b = 0 等价于 a = b
那么a到b这条链上异或等于0等价于,a到根的异或值 = b到根的异或值。
观察这颗树,(1,5)符合答案,哪些链会包括(1,5)呢?
(1,5),(3,5),(4,5),(6,5)。
可以知道(1,5)出现的次数,等于1 "右边的点数"
x 5 "左边的点数"
= 4*1
再举个例子
(2,6)这条链异或值也为0,包含(2,6)的有 (2,6),(5,6)。
等于 2"左边的点数"
x 6左边的点数"
= 2*1
所以答案为 2+4 = 6。
分两种情况讨论两点(x,y) = 0所做的贡献。
- x 和 y 的LCA是 x或者y
- x 和 y 的LCA不是 x 或者 y
对于 2 ,贡献就是 x的子树大小*y的子树大小。但还有一个前提便是x到根的异或值要等于y到根的异或值。
所以需要用一个map记录异或值为 k 时的总子树大小。
设sz[x]:为x的子树大小。
k: x到根的异或值
mp[k]: 当前异或值为k的子树大小总和
dfs整棵树,到dfs到x的时候,点x的贡献便是:sz[x] * mp[k]。
因为是有序的,所以x的贡献只会算一遍。
对于1,子树的大小并不是等于"右边的点数"或者是"左边的点数"。
需要加一个临时变量 sum。
记录此时异或值为k的情况下其"右边的点数","左边的点数"还是等于子树大小。
具体看代码吧。
代码
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<ll,ll> pii;
const int maxn = 1e5+5;
const int mod = 1e9+7;
ll ans;
vector<pii> G[maxn];
unordered_map<ll,ll> mp;
ll sz[maxn];
void dfs(int x) {
sz[x] = 1;
for(pii u : G[x]) {
dfs(u.first);
sz[x] += sz[u.first];
}
}
// 不同链
void dfs1(int x, ll xr) {
ans = (ans+1ll*mp[xr]*sz[x])%mod;
for(pii u : G[x]) {
dfs1(u.first, xr^u.second);
}
mp[xr] = (mp[xr]+sz[x])%mod;
}
ll sum = 0;
// 同链
void dfs2(int x, ll xr) {
ans = (ans+1ll*mp[xr]*sz[x])%mod;
for(pii u : G[x]) {
sum = (sum+sz[x]-sz[u.first])%mod;
mp[xr] = (mp[xr]+sum)%mod;
dfs2(u.first, xr^u.second);
mp[xr] = (mp[xr]-sum)%mod;
sum = (sum-sz[x]+sz[u.first])%mod;
}
}
int main() {
int n;
scanf("%d", &n);
for(int i = 2; i <= n; ++i) {
int fa;
ll w;
scanf("%d%lld", &fa, &w);
G[fa].push_back(pii(i,w));
}
dfs(1);
dfs1(1,0);
// printf("%lld\n", ans);
mp.clear();
dfs2(1,0);
printf("%lld\n", ans);
return 0;
}