摧毁一条边不太好搞,那就时光回溯,从后面往前面做,摧毁一条边相当于出现一条边。
连一条边 ,考虑合并这两个点所在的点集。
在连这条边之前,已经求出了点集 和 内部对答案的贡献,要处理的是跨过边 的点对的贡献,再加到总答案里。
对于 和 的点集可以分别建棵 01tire,存每个点到 的异或值,然后 dfs 一遍,求出相同的数的个数再乘起来即可。
我们不可能每次都建两颗树。考虑开始时建 棵树,每棵树先存一个 ,然后再合并两棵树。
每棵树存的就应该是点到点集中的根的异或值,换根就把树全局异或 ( 表示 到 的异或值)。
合并时,因为 为 的父亲,故 即为该点集的根,把 点集的树全局异或个 即可表示每个点到 的根的异或值,再用线段树合并的方法合并。记得记录一个 标记表示该节点子树要全异或的值,每次访问下传标记即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+50;
struct node
{
int u,v,w,nxt;
}e[N<<1];
int n,cnt,head[N],t[N*36][3],rev[N*36],tot,ro[N],fa[N],a[N],fir[N];
ll ans,res[N];
void add(int u,int v,int w)
{
e[++cnt]=(node){u,v,w,head[u]};head[u]=cnt;
e[++cnt]=(node){v,u,w,head[v]};head[v]=cnt;
}
void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
int v=e[i].v;
if(v==fa[x])continue;
fa[v]=x;fir[v]=fir[x]^e[i].w;
dfs(v);
}
}
int find(int x)
{
return x==ro[x]?x:(ro[x]=find(ro[x]));
}
void down(int x,int dep)
{
if(!rev[x])return;
if((rev[x]>>dep)&1)swap(t[x][0],t[x][1]);
rev[t[x][0]]^=rev[x];
rev[t[x][1]]^=rev[x];
rev[x]=0;
return;
}
ll dfss(int a,int b,int dep)
{
if(dep==-1)return 1ll*t[a][2]*t[b][2];
down(a,dep),down(b,dep);
ll ans=0;
if(t[a][0]&&t[b][0])ans+=dfss(t[a][0],t[b][0],dep-1);
if(t[a][1]&&t[b][1])ans+=dfss(t[a][1],t[b][1],dep-1);
return ans;
}
void build(int x)
{
for(int i=31;i>=0;i--)
{
t[x][0]=++tot;
x=tot;
}
t[x][2]++;
}
void Add(int a,int b,int dep)
{
if(dep==-1)
{
t[a][2]+=t[b][2];
return;
}
down(a,dep),down(b,dep);
if(t[a][0]&&t[b][0])Add(t[a][0],t[b][0],dep-1);
if(t[a][1]&&t[b][1])Add(t[a][1],t[b][1],dep-1);
if(!t[a][0]&&t[b][0])t[a][0]=t[b][0];
if(!t[a][1]&&t[b][1])t[a][1]=t[b][1];
return;
}
void merge(int u,int v,int w)
{
int ru=find(u),rv=v;
int re=fir[u]^fir[ru]^w;
rev[v]^=re;
ans+=dfss(ru,v,31);Add(ru,v,31);
ro[v]=ru;
}
main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
cin>>n;
int u,v,w;
for(int i=1;i<n;i++)
{
cin>>u>>v>>w;
add(u,v,w);
}
for(int i=1;i<=n;i++)ro[i]=i;
dfs(1);tot=n;
for(int i=1;i<=n;i++)build(i);
for(int i=1;i<n;i++)cin>>a[i];
for(int i=n-1;i;i--)
{
u=e[a[i]*2].u,v=e[a[i]*2].v,w=e[a[i]*2].w;
if(v==fa[u])swap(u,v);
merge(u,v,w);
res[i]=ans;
}
for(int i=1;i<=n;i++)cout<<res[i]<<'\n';
}