P8047 GALAKSIJA 题解

link\color{deeppink}link

摧毁一条边不太好搞,那就时光回溯,从后面往前面做,摧毁一条边相当于出现一条边。

连一条边 (u,v)(u,v),考虑合并这两个点所在的点集。

在连这条边之前,已经求出了点集 uuvv 内部对答案的贡献,要处理的是跨过边 (u,v)(u,v) 的点对的贡献,再加到总答案里。

对于 uuvv 的点集可以分别建棵 01tire,存每个点到 uu 的异或值,然后 dfs 一遍,求出相同的数的个数再乘起来即可。

我们不可能每次都建两颗树。考虑开始时建 nn 棵树,每棵树先存一个 00,然后再合并两棵树。

每棵树存的就应该是点到点集中的根的异或值,换根就把树全局异或 firroot xor firufir_{root}~\text{xor}~fir_ufirufir_u 表示 uu11 的异或值)。

合并时,因为 uuvv 的父亲,故 vv 即为该点集的根,把 vv 点集的树全局异或个 firrootu xor firu xor wu,vfir_{root_u}~\text{xor}~fir_u~\text{xor}~w_{u,v} 即可表示每个点到 uu 的根的异或值,再用线段树合并的方法合并。记得记录一个 lala 标记表示该节点子树要全异或的值,每次访问下传标记即可。

codecode

#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';
}