异象石 https://loj.ac/problem/10132 题目描述 思路 代码

题目描述

  给出一棵(N)个点的树,有(M)个时刻,每个时刻有三种可能的事件:(①)某个点出现异象石;(②)某个点的异象石被摧毁;(③)求使异象石所在点被联通的边集的总长度。

思路

  题目给出的使一棵树,我们考虑对于这个树确定一个根后,求出它的(dfs)序。那么事实上对于已知的异象石,我们对它按照(dfs)序排成一圈之后,所求的就是相邻两个节点的距离之和的(frac{1}{2})(然而这个结论我也不会证,只能画图感性理解)。所以我们只要为维护这个序列,并能够快速求两点间距离。求距离我们可以用倍增,而维护序列的前驱后继我们可以用平衡树维护,注意处理一下只有一个点的细节即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+10;
const ll INF=1000000000;

ll nxt[N<<1],to[N<<1],tot,w[N<<1],head[N];
void add_edge(ll x,ll y,ll v)
{
	nxt[++tot]=head[x];
	head[x]=tot;
	to[tot]=y;
	w[tot]=v;
}

ll dfn[N],dep[N],f[N][22],dis[N],idx,p[N];
void dfs(ll u,ll fa,ll s)
{
	dis[u]=s;dfn[u]=++idx;p[idx]=u;
	dep[u]=dep[fa]+1;
	for(ll i=0;i<20;i++)
		f[u][i+1]=f[f[u][i]][i];
	for(ll i=head[u];i;i=nxt[i])
	{
		ll v=to[i];
		if(v==fa)continue ;
		f[v][0]=u;
		dfs(v,u,s+w[i]);
	}
}
ll LCA(ll x,ll y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(ll i=20;i>=0;i--)
	{
		if(dep[f[x][i]]>=dep[y])x=f[x][i];
		if(x==y)return y;
	}
	for(ll i=20;i>=0;i--)
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	return f[x][0];
}
ll get_path(ll x,ll y)
{
//	cout<<x<<' '<<y<<endl;
	ll lca=LCA(x,y);
	return dis[x]+dis[y]-2*dis[lca];
}

struct Node
{
	ll lc,rc,w,v,cnt,siz;
}a[N];
ll root,sum,ss;
void upt(ll k)
{
	a[k].siz=a[a[k].lc].siz+a[a[k].rc].siz+a[k].cnt;
}
void zig(ll &k)
{
	ll t=a[k].lc;
	a[k].lc=a[t].rc;
	a[t].rc=k;
	a[t].siz=a[k].siz;
	upt(k);
	k=t;
}
void zag(ll &k)
{
	ll t=a[k].rc;
	a[k].rc=a[t].lc;
	a[t].lc=k;
	a[t].siz=a[k].siz;
	upt(k);
	k=t;
}
void insert(ll &k,ll key)
{
	if(!k)
	{
		k=++sum;
		a[k].siz=a[k].cnt=1;
		a[k].lc=a[k].rc=0;
		a[k].v=key;a[k].w=rand();
		return ;
	}
	else a[k].siz++;
	if(a[k].v==key)a[k].cnt++;
	else if(a[k].v<key)
	{
		insert(a[k].rc,key);
		if(a[a[k].rc].w<a[k].w)zag(k);
	}
	else
	{
		insert(a[k].lc,key);
		if(a[a[k].lc].w<a[k].w)zig(k);
	}
}
void f_delete(ll &k,ll key)
{
	if(a[k].v==key)
	{
		if(a[k].cnt>1)a[k].cnt--,a[k].siz--;
		else if(!a[k].lc||!a[k].rc)k=a[k].lc+a[k].rc;
		else if(a[a[k].lc].w<a[a[k].rc].w)zig(k),f_delete(k,key);
		else zag(k),f_delete(k,key);
		return ;
	}
	else a[k].siz--;
	if(a[k].v>key)
		f_delete(a[k].lc,key);
	else f_delete(a[k].rc,key);
}
ll findmin()
{
	ll x=root,res=INF;
	while(x)
	{
		res=min(res,a[x].v);
		x=a[x].lc;
	}
	return res;
}
ll findmax()
{
	ll x=root,res=-INF;
	while(x)
	{
		res=max(res,a[x].v);
		x=a[x].rc;
	}
	return res;
}
ll querypre(ll key)
{
	ll x=root,res=-INF;
	while(x)
	{
		if(a[x].v<key)res=max(res,a[x].v),x=a[x].rc;
		else x=a[x].lc;
	}
	if(res!=-INF)return res;
	else return findmax();
}
ll querynxt(ll key)
{
	ll x=root,res=INF;
	while(x)
	{
		if(a[x].v>key)res=min(res,a[x].v),x=a[x].lc;
		else x=a[x].rc;
	}
	if(res!=INF)return res;
	else return findmin();
}

ll read()
{
	ll res=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
	return res*w;
}
void write(ll x)
{
	if(x<0){putchar('-');x=-x;}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
void writeln(ll x)
{
	write(x);
	putchar('
');
}

int main() 
{
	ll n,m;
	n=read();
	for(ll i=1;i<n;i++)
	{
		ll x=read(),y=read(),z=read();
		add_edge(x,y,z);add_edge(y,x,z);
	}
	dfs(1,0,0);
	m=read();
	ll ans=0;
//	for(int i=1;i<=n;i++)
//		printf("%d ",dfn[i]);
//	printf("
");
	while(m--)
	{
		char op;
		scanf(" %c",&op);
		if(op=='+')
		{
			ll x=read();ss++;
			if(ss<=1){insert(root,dfn[x]);ans=0;continue ;}
			ll l=querypre(dfn[x]),r=querynxt(dfn[x]);
//			cout<<l<<' '<<r<<endl;
			l=p[l];r=p[r];
			insert(root,dfn[x]);
			ans=ans+get_path(l,x)+get_path(x,r)-get_path(l,r);
		}
		else if(op=='-')
		{
			ll x=read();ss--;
			if(ss<=1){f_delete(root,dfn[x]);ans=0;continue ;}
			ll l=querypre(dfn[x]),r=querynxt(dfn[x]);
//			cout<<l<<' '<<r<<endl;
			l=p[l];r=p[r];
			f_delete(root,dfn[x]);
			ans=ans-get_path(l,x)-get_path(x,r)+get_path(l,r);
		}
		else
			writeln(ans/2);	
//		cout<<ans<<endl;
	}
	return 0;
}