BZOJ3991: [SDOI2015]寻宝游戏

BZOJ3991: [SDOI2015]寻宝游戏

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

题解Here!

其实题面让我想起了虚树。
但是数据范围中没有什么$\sum$之类的东西啊???
感觉除了暴力重建虚树没有什么好办法了。。。
复杂度自然是爆炸的。。。
所以回头看题。
假设当前我们选出来了一些点:$a_1,a_2,a_3,\cdots,a_k$
顺序当然是按欧拉序排序。
显然我们要求的是:$$Ans=dis(a_1,a_2)+dis(a_2,a_3)+\cdots+dis(a_{k-1},a_k)+dis(a_k,a_1)$$
假设我们当前要把$x$选中。
设$x$在${a_i}$中欧拉序上的前一个,后一个分别为$y,z$。
那么新的答案就是:$$Ans\_new=dis(a_1,a_2)+dis(a_2,a_3)+\cdots+dis(y,x)+dis(x,z)+\cdots+dis(a_{k-1},a_k)+dis(a_k,a_1)$$
而原来的答案可以表示为:$$Ans=dis(a_1,a_2)+dis(a_2,a_3)+\cdots+dis(y,z)+\cdots+dis(a_{k-1},a_k)+dis(a_k,a_1)$$
是不是发现了什么?
对,就是这个:$$Ans\_new=Ans+dis(y,x)+dis(x,z)-dis(y,z)$$
这不就好维护了?
于是开一个$set$大力维护这个序列,每次找到前驱、后继即可。
其实$vector$按理说也能维护,不过可能是我太菜了吧。。。
注意这个序列是循环的,所以注意一下边界问题。
至于$LCA$以及$dis(x,y)$怎么搞。。。树剖不多说。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<set>
#define MAXN 100010
using namespace std;
set<int> point;
set<int>::iterator it,front,after;
int n,m,c=1,d=1;
int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN];
long long sum=0,dis[MAXN];
bool treasure[MAXN];
struct Tree{
	int next,to,w;
}a[MAXN<<1];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline void add(int u,int v,int w){
	a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
	a[c].to=u;a[c].w=w;a[c].next=head[v];head[v]=c++;
}
void dfs1(int rt){
	son[rt]=0;size[rt]=1;
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(!deep[will]){
			deep[will]=deep[rt]+1;
			dis[will]=dis[rt]+a[i].w;
			fa[will]=rt;
			dfs1(will);
			size[rt]+=size[will];
			if(size[will]>size[son[rt]])son[rt]=will;
		}
	}
}
void dfs2(int rt,int f){
	id[rt]=d++;pos[id[rt]]=rt;top[rt]=f;
	if(son[rt])dfs2(son[rt],f);
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	return x;
}
inline long long get_dis(int x,int y){
	return dis[x]+dis[y]-2*dis[LCA(x,y)];
}
inline long long add_dis(int x,int y,int z){
	return get_dis(x,y)+get_dis(x,z)-get_dis(y,z);
}
void solve(int x){
	it=point.find(id[x]);
	if(it==point.begin())front=point.end();
	else front=it;
	front--;
	after=it;
	after++;
	if(after==point.end())after=point.begin();
}
void work(){
	int x,y,z;
	while(m--){
		x=read();
		if(treasure[x]){
			solve(x);
			y=pos[*front];z=pos[*after];
			sum-=add_dis(x,y,z);
			point.erase(id[x]);
			treasure[x]=false;
		}
		else{
			point.insert(id[x]);
			treasure[x]=true;
			solve(x);
			y=pos[*front];z=pos[*after];
			sum+=add_dis(x,y,z);
		}
		printf("%lld\n",sum);
	}
}
void init(){
	int u,v,w;
	n=read();m=read();
	for(int i=1;i<n;i++){
		u=read();v=read();w=read();
		add(u,v,w);
	}
	deep[1]=1;dis[1]=0;
	dfs1(1);
	dfs2(1,1);
}
int main(){
	init();
	work();
	return 0;
}

 

posted @ 2019-03-31 18:55  符拉迪沃斯托克  阅读(203)  评论(0编辑  收藏  举报
Live2D