bzoj 3991: [SDOI2015]寻宝游戏 — dfs序,set

3991: [SDOI2015]寻宝游戏

Time Limit: 40 Sec  Memory Limit: 128 MB

Description

 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物

Input

 第一行,两个整数N、M,其中M为宝物的变动次数。

接下来的N-1行,每行三个整数x、y、z,表示村庄x、y之间有一条长度为z的道路。
接下来的M行,每行一个整数t,表示一个宝物变动的操作。若该操作前村庄t内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。

Output

 M行,每行一个整数,其中第i行的整数表示第i次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出0。

Sample Input

4 5
1 2 30
2 3 50
2 4 60
2
3
4
2
1

Sample Output

0
100
220
220
280

HINT

 1<=N<=100000

1<=M<=100000
对于全部的数据,1<=z<=10^9

Source

可以证明一个结论,最优方案就是把所有关键点排序,相邻点的路径和加上两端点的路径

那么就是可以维护一个set,维护dfs序,每次操作就是插入和删除,计算相邻点的变化就好

#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1000000007
#define ll long long
#define N 200010
inline int rd()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int lj[N],fro[N],to[N],cnt;
ll v[N];
void add(int a,int b,int c){fro[++cnt]=lj[a];to[cnt]=b;lj[a]=cnt;v[cnt]=c;}
int n,m;
int dep[N],dfn[N],tim;
int fa[N][21];
ll dis[N];
void dfs(int x,int f)
{
	dep[x]=dep[f]+1;
	dfn[x]=++tim;
	fa[x][0]=f;
	for(int i=1;i<21;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=lj[x];i;i=fro[i])
	{
		if(to[i]==f) continue;
		dis[to[i]]=dis[x]+v[i];
		dfs(to[i],x);
	}
}
set<pair<int,int> >s;
set<pair<int,int> >::iterator it,bg,ed,b2,i2,i1;
#define mp make_pair
bool vs[N];
ll ans;
int LCA(int a,int b)
{
	if(dep[a]<dep[b]) swap(a,b);
	for(int i=20;i>=0;i--) if(dep[fa[a][i]]>=dep[b]) a=fa[a][i];
	if(a==b) return a;
	for(int i=20;i>=0;i--) if(fa[a][i]^fa[b][i]) a=fa[a][i],b=fa[b][i];
	return fa[a][0];
}
ll fd(int a,int b)
{
	int l=LCA(a,b);
	return dis[a]+dis[b]-dis[l]*2;
}
int main()
{
	n=rd();m=rd();
	for(int i=1,x,y,z;i<n;i++)
	{
		x=rd();y=rd();z=rd();
		add(x,y,z);add(y,x,z);
	}
	dfs(1,0);
	int x,y,z;
	pair<int,int>p;
	while(m--)
	{
		x=rd();
		p=mp(dfn[x],x);
		b2=bg=s.begin();ed=s.end();
		if(s.size()) b2++,ed--;
		if(!vs[x])
		{
			vs[x]=1;
			if(!s.size()); 
			else if(s.size()==1) ans=fd((*bg).second,x)*2;
			else 
			{
				i1=it=s.lower_bound(p);
				if(it==s.end()) i1--,y=(*bg).second,z=(*i1).second;
				else if(it==s.begin()) y=(*bg).second,z=(*ed).second;
				else i1--,y=(*i1).second,z=(*it).second;
				ans-=fd(y,z);
				ans+=fd(x,y)+fd(x,z);
			}
			s.insert(p);
		}
		else 
		{
			vs[x]=0;
			if(s.size()<=2) ans=0;
			else 
			{
				i1=i2=it=s.lower_bound(p);i2++;
				if(i2==s.end()) i1--,y=(*bg).second,z=(*i1).second;
				else if(it==s.begin()) y=(*b2).second,z=(*ed).second;
				else i1--,y=(*i1).second,z=(*i2).second;
				ans+=fd(y,z);
				ans-=fd(x,y)+fd(x,z);
			}
			s.erase(p);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

 

评论

还没有任何评论,你来说两句吧

发表评论

衫小寨 出品