前言
很基础的一道树形DP题。
题目
题目大意:
给定一棵 (N) 个点的带权树,询问每个无序点对间的异或距离和。答案对 (10^9+7) 取模。
(2le Nle 2 imes 10^5;0le w_ile 2^{60}.)
讲解
先考虑边权是 (0,1) 怎么做。
显然可以直接DP,令 (dp[i][j]) 表示从下往上到 (i) 异或距离为 (j) 的路径数。转移很显然:
[dp[u][i]=underset{vin son_x}{sum}dp[v][ioplus w_{u,v}]+(ioplus w_{u,v}oplus 1)
]
中途顺便统计答案即可。
由于每个二进制位之间不会互相影响,所以我们对于每一位单独做即可。
时间复杂度:(O(nlog_2max{w_i}))。
当然你也可以从上往下做,用经典结论 (dis_{1,u}oplus dis_{1,v}=dis{u,v}) 计数。
其实本质是相同的。
注意long long
的使用以及溢出问题。
代码
从下往上。
int head[MAXN],tot;
struct edge
{
int v,nxt;
LL w;
}e[MAXN << 1];
void Add(LL x){ans = (ans + x) % MOD;}
void Add_Edge(int x,int y,LL z)
{
e[++tot].v = y;
e[tot].w = z;
e[tot].nxt = head[x];
head[x] = tot;
}
void Add_Double_Edge(int x,int y,LL z)
{
Add_Edge(x,y,z);
Add_Edge(y,x,z);
}
LL dp[MAXN][2];
void dfs(int x,int fa,int o)
{
dp[x][0] = dp[x][1] = 0;
for(int i = head[x]; i ;i = e[i].nxt)
{
int v = e[i].v,w = (e[i].w >> o) & 1;
if(v == fa) continue;
dfs(v,x,o);
if(!w)
{
Add(dp[x][0] * dp[v][1] % MOD * ((1ll << o) % MOD));
Add(dp[x][1] * (dp[v][0]+1) % MOD * ((1ll << o) % MOD));
dp[x][0] += dp[v][0]+1;
dp[x][1] += dp[v][1];
}
else
{
Add(dp[x][0] * (dp[v][0]+1) % MOD * ((1ll << o) % MOD));
Add(dp[x][1] * dp[v][1] % MOD * ((1ll << o) % MOD));
dp[x][0] += dp[v][1];
dp[x][1] += dp[v][0]+1;
}
}
Add(dp[x][1] * ((1ll << o) % MOD));
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
for(int i = 1;i < n;++ i)
{
int u = Read(),v = Read();
Add_Double_Edge(u,v,Read());
}
for(int i = 0;i <= 60;++ i) dfs(1,0,i);
Put(ans);
return 0;
}