【BZOJ】3991: [SDOI2015]寻宝游戏
题意
给一个\(n\)个点带边权的树。有\(m\)次操作,每一次操作一个点\(x\),如果\(x\)已经出现,则\(x\)消失。否则\(x\)出现。每一操作后,询问从某个点开始走,直到经过所有出现的点,最后再回到开始的那个点的最短路程。
分析
首先容易知道我们选任意一个在某两点路径上的点作为起点都能得到最优解(包括端点)。我们只需要考虑走的顺序。
题解
由于按照dfs序的走法是最短的,因此我们按dfs序维护一下前前后后的距离和即可。如何证明?好像并不会...
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
int n, ihead[N], FF[N], dep[N], cnt, a[N], fa[N][17], m;
ll d[N];
struct E {
	int next, to, w;
}e[N<<1];
struct dat {
	int FF, id;
	ll dis;
	bool operator < (const dat &a) const {
		return FF<a.FF;
	}
};
set<dat> s;
void add(int x, int y, int w) {
	e[++cnt]=(E){ihead[x], y, w}; ihead[x]=cnt;
	e[++cnt]=(E){ihead[y], x, w}; ihead[y]=cnt;
}
void dfs(int x, int f=0) {
	static int fid=0;
	FF[x]=++fid;
	for(int i=1; i<=16; ++i) {
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(int i=ihead[x]; i; i=e[i].next) {
		int y=e[i].to;
		if(y==f) {
			continue;
		}
		fa[y][0]=x;
		dep[y]=dep[x]+1;
		d[y]=d[x]+e[i].w;
		dfs(y, x);
	}
}
int LCA(int x, int y) {
	if(dep[x]<dep[y]) {
		swap(x, y);
	}
	int d=dep[x]-dep[y];
	for(int i=16; i>=0; --i) if((d>>i)&1) x=fa[x][i];
	if(x==y) return x;
	for(int i=16; i>=0; --i) if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];
	return fa[x][0];
}
ll getdis(int x, int y) {
	int lca=LCA(x, y);
	return d[x]+d[y]-(d[lca]<<1);
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i=1; i<n; ++i) {
		int x, y, w;
		scanf("%d%d%d", &x, &y, &w);
		add(x, y, w);
	}
	dfs((n+1)>>1);
	ll ans=0;
	while(m--) {
		int p;
		scanf("%d", &p);
		dat t;
		if(a[p]) {
			set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end(), itb=s.end();
			itb=it;
			++itb;
			ans-=it->dis;
			if(it!=s.begin()) {
				itp=it;
				--itp;
			}
			if(itb!=s.end()) {
				ans-=itb->dis;
				if(itp==s.end()) {
					t=*itb;
					t.dis=0;
					s.erase(itb);
					s.insert(t);
				}
				else {
					t=*itb;
					t.dis=getdis(itb->id, itp->id);
					ans+=t.dis;
					s.erase(itb);
					s.insert(t);
				}
			}
			s.erase(it);
			a[p]=0;
		}
		else {
			set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end();
			if(it!=s.begin()) {
				itp=it;
				--itp;
			}
			if(it!=s.end()) {
				if(itp!=s.end()) {
					ll dis=getdis(p, itp->id);
					s.insert((dat){FF[p], p, dis});
					ans+=dis;
				}
				else {
					s.insert((dat){FF[p], p, 0});
				}
				t=*it;
				ans-=t.dis;
				t.dis=getdis(p, it->id);
				ans+=t.dis;
				s.erase(it);
				s.insert(t);
			}
			else {
				if(itp==s.end()) {
					s.insert((dat){FF[p], p, 0});
				}
				else {
					ll dis=getdis(p, itp->id);
					s.insert((dat){FF[p], p, dis});
					ans+=dis;
				}
			}
			a[p]=1;
		}
		ll temp=0;
		if(s.size()>=2) {
			set<dat>::iterator it=s.end();
			--it;
			temp=getdis(s.begin()->id, it->id);
		}
		printf("%lld\n", ans+temp);
	}
	return 0;
}【BZOJ】3991: [SDOI2015]寻宝游戏的更多相关文章
- bzoj 3991: [SDOI2015]寻宝游戏 虚树 set
		目录 题目链接 题解 代码 题目链接 bzoj 3991: [SDOI2015]寻宝游戏 题解 发现每次答案就是把虚树上的路径*2 接在同一关键点上的点的dfs序是相邻的 那么用set动态维护dfs序 ... 
- 树形结构的维护:BZOJ 3991: [SDOI2015]寻宝游戏
		Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ... 
- bzoj 3991: [SDOI2015]寻宝游戏
		Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ... 
- BZOJ 3991: [SDOI2015]寻宝游戏 树链的并+set
		Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ... 
- [BZOJ 3991][SDOI2015]寻宝游戏(dfs序)
		题面 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路 ... 
- BZOJ 3991: [SDOI2015]寻宝游戏 [虚树 树链的并 set]
		传送门 题意: $n$个点的树,$m$次变动使得某个点有宝物或没宝物,询问每次变动后集齐所有宝物并返回原点的最小距离 转化成有根树,求树链的并... 两两树链求并就可以,但我们按照$dfs$序来两两求 ... 
- BZOJ.3991.[SDOI2015]寻宝游戏(思路 set)
		题目链接 从哪个点出发最短路径都是一样的(最后都要回来). 脑补一下,最短路应该是按照DFS的顺序,依次访问.回溯遍历所有点,然后再回到起点. 即按DFS序排序后,Ans=dis(p1,p2)+dis ... 
- 3991: [SDOI2015]寻宝游戏
		3991: [SDOI2015]寻宝游戏 https://www.lydsy.com/JudgeOnline/problem.php?id=3991 分析: 虚树+set. 要求树上许多点之间的路径的 ... 
- 【BZOJ】3991: [SDOI2015]寻宝游戏 虚树+DFS序+set
		[题意]给定n个点的带边权树,对于树上存在的若干特殊点,要求任选一个点开始将所有特殊点走遍后返回.现在初始没有特殊点,m次操作每次增加或减少一个特殊点,求每次操作后的总代价.n,m<=10^5. ... 
随机推荐
- SVN如何查看修改的文件记录]	           来源:Linux社区     作者:frogoscar
			SVN如何查看修改的文件记录 [日期:2014-11-20] 来源:Linux社区 作者:frogoscar [字体:大 中 小] 主要是有四个命令,svn log用来展示svn 的版本作者 ... 
- ubuntu系统修改mysql字符集
			1.进入mysql,查看默认字符集: mysql>show variables like 'char%'; 2.退出mysql; 3.输入命令:sudo gedit /etc/mysql/con ... 
- 【转~】初识贝塞尔曲线(Bézier curve)
			本文图文大多转自http://www.html-js.com/article/1628 QAQ我居然去扒维基,,,看不懂啊,,,我要去补数学,,, 在做变形小鸡的时候用到CSS3 transition ... 
- opencv常见代码
			http://blog.csdn.net/lyc_daniel/article/details/16883707 
- python 3.x  urllib学习
			urllib.request import urllib.request as ur url='http://ie.icoa.cn' user_agent = 'Mozilla/4.0 (compat ... 
- jstl param   url   redirect  import
			import标签 import标签用来导入其他页面 属性: * url :引入页面的路径 * context :工程名 * var :将引入的页面保存到一个变量中 * scope :保存到一个作用域中 ... 
- 关于Promise模式  整理中。。。
			http://blog.csdn.net/womendeaiwoming/article/details/49849055 研究了几天Promise模式,因为在项目里也遇到了所谓的“回调陷阱”,就是多 ... 
- Openstack4j 在 Maven 中的构建
			什么是 Openstack4j ? OpenStack的官方SDK是基于Python语言的,对于Java程序猿来说,将Python翻译过来未免麻烦.在Openstack官方的Wiki中(戳我直达),我 ... 
- macbook air 开机黑屏解决方法
			故障现象:1. 开机有声音2. 背面logo亮灯3. 键盘背光灯不亮4. 大写锁定键按下不亮5. 屏幕黑屏,无苹果logo 解决:重置PRAM后成功开机. 1. 关闭 Mac.2. 在键盘上找到以下按 ... 
- php上传图片文件常用的几个方法
			1. 前台 <form class="add-form" method="post" action="/person/save" en ... 
