现场赛时看到4个求和就直接跳过了,其实并不难,没有太大的思维量
(但我不会写这个dfs,等于白给)
题解:预处理每个点到根节点的路径异或和,记为
v
a
l
[
u
]
val[u]
val[u],两个点对答案有贡献时这个点的
v
a
l
val
val相等。
若
v
a
l
[
u
]
=
v
a
l
[
v
]
val[u] = val[v]
val[u]=val[v],那么
u
,
v
u,v
u,v 对总答案的贡献就等于经过
u
,
v
u,v
u,v 的路径数
根据
u
,
v
u,v
u,v 所在位置可以分为两种情况:
1.
u
,
v
u,v
u,v同链,即
l
c
a
(
u
,
v
)
=
u
lca(u,v) = u
lca(u,v)=u ||
l
c
a
(
u
,
v
)
=
v
lca(u,v) = v
lca(u,v)=v
2.
u
,
v
u,v
u,v不同链
对于第二种情况,
u
,
v
u,v
u,v对答案的贡献就等于
s
z
[
u
]
∗
s
z
[
v
]
sz[u] * sz[v]
sz[u]∗sz[v],
s
z
[
u
]
sz[u]
sz[u]为
u
u
u 点的子树大小
对于第一种情况,假设
u
u
u是
v
v
v 的祖先,
s
s
s 是
u
u
u ->
v
v
v 路径上的一个儿子,则
u
,
v
u,v
u,v对答案的贡献为
(
n
−
s
z
[
s
]
)
∗
s
z
[
v
]
(n - sz[s]) * sz[v]
(n−sz[s])∗sz[v]
d
f
s
dfs
dfs是沿着一条链遍历下去,基于
d
f
s
dfs
dfs的过程,可以将上述两种贡献分开计算:
1.当计算同链贡献时,只维护当前
d
f
s
dfs
dfs点到根节点这条链的结点信息,退出访问时情况这个点的信息,这样保证链上的点的贡献只来自于这条链。
2.当计算不同链贡献时,只有当前点退出
d
f
s
dfs
dfs时才维护这个点的信息,这样保证链上的点的贡献不来自于这条链。
具体看代码
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
#define pii pair<ll,ll>
#define fir first
#define sec second
typedef long long ll;
map<ll,ll> mp;
vector<pii> g[maxn];
int n,sz[maxn];
ll val[maxn];
ll ans = 0;
void prework(int u,int fa) {
if(u == 1) val[u] = 0;
sz[u] = 1;
for(auto it : g[u]) {
if(it.fir == fa) continue;
val[it.fir] = val[u] ^ it.sec;
prework(it.fir,u);
sz[u] += sz[it.fir];
}
}
void dfs1(int u,int fa) { //计算同链贡献
ans = (ans + 1ll * mp[val[u]] * sz[u] % mod) % mod;
for(auto it : g[u]) {
if(it.fir == fa) continue;
mp[val[u]] = (mp[val[u]] + n - sz[it.fir] + mod) % mod;
dfs1(it.fir,u);
mp[val[u]] = (mp[val[u]] - n + sz[it.fir] + mod) % mod;
}
}
void dfs2(int u,int fa) { //计算不同链的贡献
ans = (ans + 1ll * mp[val[u]] * sz[u] % mod) % mod;
for(auto it : g[u]) {
if(it.fir == fa) continue;
dfs2(it.fir,u);
}
mp[val[u]] = (mp[val[u]] + sz[u]) % mod;
}
int main() {
scanf("%d",&n);
for(int i = 2; i <= n; i++) {
ll u,w;scanf("%lld%lld",&u,&w);
g[u].push_back(pii(i,w));
g[i].push_back(pii(u,w));
}
prework(1,0);
dfs1(1,0);
mp.clear();
dfs2(1,0);
printf("%lld\n",ans);
return 0;
}