思路
首先統計當前結點的子樹有多少個結點sum(包括自己),以及統計從根走到當前結點的xor,因爲兩點的xor值和從兩點走到根的xor值是一樣的。
情況分爲兩種,一種是兩個點在同一條鏈上,一種是在兩條鏈上。
用map 維護 同鏈或者不同鏈的xor相等的個數。
在不同鏈的情況,在先序位置求ans,在後序map加上數值,這樣以後的點每次求and的時候,map中一定是與該點不同鏈的點貢獻的值,而且題目中u<v,有方向,所以map中不同鏈,而且都是該點DFS序前面的點,符合要求。
同一條鏈的情況,在先序位置求ans,在for循環中的dfs前map加上數值,dfs後減去數值,類似於走迷宮的vis標記。這樣dfs前加標記,dfs後清除標記,就會使走的路是一條長鏈,所以map中的都是一條鏈上的。
參考:詳細的推導過程
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1000010;
const int mod = 1e9+7;
struct Edge
{
int to,next; ll w;
}edge[N];
int head[N],tot=0,n;
void addEdge(int from,int to,ll w)
{
edge[tot].to = to; edge[tot].w = w;
edge[tot].next = head[from];
head[from] = tot++;
}
ll Xor[N],sum[N],ans;
map <ll,ll> mp;
void dfsnum(int u)
{
sum[u] = 1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].to;
ll w = edge[i].w;
Xor[v] = Xor[u]^w;
dfsnum(v);
sum[u] += sum[v];
}
}
void dfs1(int u)//不同鏈
{
ans = (ans + sum[u]*mp[Xor[u]]%mod)%mod;
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].to;
dfs1(v);
}
mp[Xor[u]] = (mp[Xor[u]] + sum[u])%mod;
}
void dfs2(int u)//同鏈
{
ans = (ans + sum[u]*mp[Xor[u]]%mod)%mod;
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].to;
ll t = sum[1]-sum[v];
mp[Xor[u]] = (mp[Xor[u]] + t)%mod;
dfs2(v);
mp[Xor[u]] = (mp[Xor[u]] - t + mod)%mod;
}
}
int main()
{
tot = 0;
memset(head,-1,sizeof(head));
scanf("%d",&n);
int fa; ll w;
for(int i=2;i<=n;i++){
scanf("%d%lld",&fa,&w);
addEdge(fa,i,w);
}
dfsnum(1);
mp.clear();
dfs1(1);
mp.clear();
dfs2(1);
printf("%lld\n",ans);
return 0;
}