[bzoj3991][SDOI2015]寻宝游戏_树链的并_倍增lca_平衡树set

寻宝游戏 bzoj-3991 SDOI-2015

题目大意题目链接

注释:略。



想法:我们发现如果给定了一些点有宝物的话那么答案就是树链的并。

树链的并的求法就是把所有点按照$dfs$序排序然后相加再减去相邻之间的$lca$。

故此我们按照$dfs$序维护一个平衡树。

每次往里插入节点即可。

实时用$lca$更新答案,复杂度$O(nlogn)$。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 100010
using namespace std; typedef long long ll;
set<int>s;
int head[N],nxt[N<<1],to[N<<1],tot; ll val[N<<1];
ll dis[N],ans; int dic[N],f[22][N],size[N],re[N],dep[N],cnt;
bool vis[N];
inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
ll rd() {ll x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;}
inline void add(int x,int y,ll z) {to[++tot]=y; val[tot]=z; nxt[tot]=head[x]; head[x]=tot;}
void dfs(int pos,int fa)
{
	f[0][pos]=fa; for(int i=1;i<=20;i++) f[i][pos]=f[i-1][f[i-1][pos]];
	dep[pos]=dep[fa]+1; dic[pos]=++cnt,re[cnt]=pos; size[pos]=1; for(int i=head[pos];i;i=nxt[i]) if(to[i]!=fa)
	{
		dis[to[i]]=dis[pos]+val[i]; dfs(to[i],pos); size[pos]+=size[to[i]];
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;~i;i--) if(dep[f[i][x]]>=dep[y]) x=f[i][x];
	if(x==y) return x;
	for(int i=20;~i;i--) if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
	return f[0][x];
}
inline void output()
{
	set<int>::iterator it=s.begin();
	for(;it!=s.end();it++) printf("%d ",*it); puts("");
}
int main()
{
	int n=rd(),m=rd(); for(int i=1;i<n;i++) {int x=rd(),y=rd(); ll z=rd(); add(x,y,z); add(y,x,z);}
	dfs(1,1);
	// for(int i=1;i<=n;i++) printf("%d %d %d %d %d %d\n",re[dic[i]],dis[i],dic[i],dep[i],size[i],f[0][i]);
	for(int i=1;i<=m;i++)
	{
		int x=rd();
		if(s.empty()) {s.insert(dic[x]),ans=dis[x]; vis[x]=true;}
		else if(!vis[x])
		{
			vis[x]=true;
			set<int>::iterator it1=s.lower_bound(dic[x]);
			set<int>::iterator it2=s.upper_bound(dic[x]);
			if(it2==s.end())
			{
				it1--;
				ans+=dis[x]-dis[lca(x,re[*it1])];
			}
			else
			{
				if(it1==s.begin()) ans+=dis[x]-dis[lca(x,re[*it1])];
				else
				{
					it1--;
					int y=re[*it1],z=re[*it2];
					ans+=dis[x]+dis[lca(y,z)]-dis[lca(x,y)]-dis[lca(x,z)];
				}
			}
			s.insert(dic[x]);
		}
		else
		{
			vis[x]=false;
			set<int>::iterator it1=s.lower_bound(dic[x]);
			set<int>::iterator it2=s.upper_bound(dic[x]);
			if(it2==s.end())
			{
				if(it1==s.begin()) ans=0;
				else
				{
					it1--;
					int y=re[*it1];
					ans+=dis[lca(x,y)]-dis[x];
				}
			}
			else
			{
				if(it1==s.begin())
				{
					int z=re[*it2];
					ans+=dis[lca(x,z)]-dis[x];
				}
				else
				{
					it1--;
					int y=re[*it1],z=re[*it2];
					ans+=dis[lca(y,x)]+dis[lca(x,z)]-dis[x]-dis[lca(y,z)];
				}
			}
			s.erase(dic[x]);
		}
		// output();
		// cout << ans << endl ;
		set<int>::iterator it1=s.begin();
		set<int>::iterator it2=s.end();
		if(it1==it2) puts("0");
		else
		{
			it2--;
			if(it1==it2) puts("0");
			else printf("%lld\n",(ans-dis[lca(re[*it1],re[*it2])])*2);
		}
	}
	return 0;
}

小结:好题啊好题啊。

原文地址:https://www.cnblogs.com/ShuraK/p/10163820.html

时间: 2024-08-04 05:56:38

[bzoj3991][SDOI2015]寻宝游戏_树链的并_倍增lca_平衡树set的相关文章

[BZOJ3991][SDOI2015]寻宝游戏

睡前写题解.此题Pascal党有难度max的buff……毕竟C++有set这种黑科技,P党只好手写平衡树.然而这还不是最主要的,最大的代码难点在于此题有奇怪的边界处理,加了一堆特判,用封好的set大概会清晰很多. 脑补一下就会发现答案就是所有被选中的点所构成的虚树的边权和的两倍,走路的过程就是在虚树上dfs的过程.这样我们可以把原树先dfs一遍,在虚树中dfs的时候访问关键点的先后顺序与原树中dfs是一致的,这样我们对于一些关键点可以把它们按照dfs序从前往后排,由第一个点开始走,一个一个往后依

【dfs序】【set】bzoj3991 [Sdoi2015]寻宝游戏

在考试代码的基础上稍微改改就a了……当时为什么不稍微多想想…… 插入/删除一个新节点时就把其dfn插入set. 当前的答案就是dfn上相邻的两两节点的距离和,再加上首尾节点的距离. 比较显然?不会证明…… 貌似叫“虚树”? #include<cstdio> #include<set> using namespace std; #define N 100001 typedef long long ll; set<int>S; typedef set<int>:

【BZOJ3991】[SDOI2015]寻宝游戏 树链的并+set

[BZOJ3991][SDOI2015]寻宝游戏 Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止.小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程.但是这个游戏中宝物经常变化,有时某个村庄中会突

【BZOJ 3991】 [SDOI2015]寻宝游戏

3991: [SDOI2015]寻宝游戏 Time Limit: 40 Sec Memory Limit: 128 MB Submit: 251 Solved: 137 [Submit][Status][Discuss] Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直

P3320 [SDOI2015]寻宝游戏

题目 P3320 [SDOI2015]寻宝游戏 做法 很巧妙的一种思路,懂了之后觉得大水题 首先要知道:在一棵树上标记一些点,然后从任意一点出发,遍历所有的的最小路径为\(dfs\)序从小到大遍历 那就把点丢到\(set\)里面,然后找\(dfs\)的前驱与后继计算路径就好了 其实也有点虚树的思想,只管标记的这几个点 My complete code #include<cstdio> #include<iostream> #include<algorithm> #inc

异象石/[SDOI2015]寻宝游戏

AcWing 异象石 洛咕 寻宝游戏 题意:Adera是Microsoft应用商店中的一款解谜游戏. 异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图. 这张地图上有\(N(N<=1e5)\)个点,有\(N-1\)条双向边把它们连通起来. 起初地图上没有任何异象石,在接下来的\(M(M<=1e5)\)个时刻中,每个时刻会发生以下三种类型的事件之一: 地图的某个点上出现了异象石(已经出现的不会再次出现); 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点); 向玩家询问

BZOJ 3991 Sdoi2015 寻宝游戏 树链的并

题目大意:给定一棵树,多次将某个点设为关键点或取消关键点,求虚树中边长总和的二倍 Orz wyfcyx 首先我们考虑树链的并(每个点到根节点的链的并集)怎么求 将虚树中的所有点按照DFS序排序,将每个点的深度统计入答案,将相邻两个点之间的LCA的深度从答案中扣除,就是所有点到根的链的并集的长度 但是我们要求的是虚树中的边长总和,因此我们还要减掉所有点LCA的深度 现在要求动态维护,因此我们用set维护一下虚树的DFS序即可 各种开小数组忘开long long忘改lld我这是怎么了-- #incl

bzoj 3991: [SDOI2015]寻宝游戏

Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止.小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程.但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小

Luogu P3320 [SDOI2015]寻宝游戏 / 异象石 【LCA/set】

期末考试结束祭! 在期末考试前最后一发的测试中,异象石作为第二道题目出现QAQ.虽然知道是LCA图论,但还是敲不出来QAQ. 花了两天竞赛课的时间搞懂(逃 异象石(stone.pas/c/cpp)题目描述Adera 是 Microsoft 应用商店中的一款解谜游戏.异象石是进入 Adera 中异时空的引导物,在 Adera 的异时空中有一张地图.这张地图上有 N 个点,有 N-1 条双向边把它们连通起来.起初地图上没有任何异象石,在接下来的 M个时刻中,每个时刻会发生以下三种类型的事件之一:1.