[bzoj3991] [洛谷P3320] [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 \leq N \leq100000\)
\(1 \leq M \leq 100000\)
对于全部的数据,\(1 \leq z \leq10^9\)
想法
由于题目中要求找到所有宝物后要返回起点,故走过的每条路都走了2遍。
如果我们把所有有宝物的村庄(不妨称它们为关键节点)及它们的 \(lca\) 拎出来,单独建一棵树,我们要走的所有边便是这棵树上的所有边。
比如下图(蓝色的为关键节点):

我们叫这棵树”虚树“。
让我们先回顾一下虚树的建树过程:
现在原树上 \(dfs\) 求出每个点的 \(dep\) 及 \(dfs序\)
然后将关键点按 \(dfs序\) 排序,依次考虑
用一个栈维护根到当前点 \(p\) 的路径
插入下一个点 \(q\) 时,根到 \(q\) 的路径中,\(q\) 的父节点为 \(lca(p,q)\)
于是就在原来根到 \(p\) 的路径中找合适的位置将 \(lca(p,q)\) (如果原路径中有就不用)及 \(q\) 插进去就行了。
这其中关键一步是“按 \(dfs序\) 排序”
而在这道题中,虚树的边数的二倍 便是按 \(dfs序\) 排序后的相邻的关键点之间的距离和(包括最后一个关键点与第一个关键点之间的距离)
(画个图就可理解了。。。每条边相当于在“进入”和“离开”时各经过一次)
那么我们只要对虚树中排序后所有相邻关键点求一遍距离,再加起来就行了。
但题目中还有修改,怎么办?
注意到每次修改只改一个点——如果这个点在虚树中,删掉它影响的只是虚树中与它相邻的两个关键点;如果不在虚树中,影响的也只是它插到虚树中后与它相邻的两个点。
于是我们可以用一个 \(set\) 来维护虚树中点的 \(dfs序\) ,每次修改只需在 \(set\) 中 \(O(logn)\) 寻找这个点的前驱后继。
如果要把这个点加到虚树中,\(ans\) 减去它相邻两个点之间的距离,加上它分别到相邻两个点的距离
如果要把这个点从虚树中删除,\(ans\) 减去它分别到相邻两个点的距离,加上它相邻两个点之间的距离
代码
一直用 \(set\) 不是很熟练
这次 \(get\) 了新技能:
y=*--st.lower_bound(x); //找小于x的最大数
z=*st.upper_bound(x); //找大于x的最小数
还有一个小技巧是在 \(set\) 中先加入 \(inf 与 -inf\) ,避免出现奇怪问题
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
#define INF 1000000
using namespace std;
typedef long long ll;
const int N = 100005;
struct node{
int v,len;
node *next;
}pool[N*2],*h[N];
int cnt;
void addedge(int u,int v,int l){
node *p=&pool[++cnt],*q=&pool[++cnt];
p->v=v;p->next=h[u];h[u]=p;p->len=l;
q->v=u;q->next=h[v];h[v]=q;q->len=l;
}
int n,m,vis[N];
int f[N][20],dep[N],dfn[N],re[N],tot;
ll sum[N];
void dfs(int u){
int v;
dfn[u]=++tot; re[tot]=u;
for(node *p=h[u];p;p=p->next)
if(!dep[v=p->v]){
dep[v]=dep[u]+1;
f[v][0]=u; sum[v]=sum[u]+p->len;
for(int j=1;j<20;j++)
f[v][j]=f[f[v][j-1]][j-1];
dfs(v);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
inline ll Sum(int x,int y) { return sum[x]+sum[y]-sum[lca(x,y)]*2; }
set<int> st;
int main()
{
int x,y,z;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
dep[1]=1;
dfs(1);
int s,p,q;
ll t=0;
st.insert(-INF); st.insert(INF);
while(m--){
scanf("%d",&x);
if(vis[x]==0){
vis[x]=1;
s=st.size();
if(s==2) { st.insert(dfn[x]); printf("0\n"); continue; }
p=*--st.lower_bound(dfn[x]); q=*st.upper_bound(dfn[x]);
if(p==-INF) p=*--(--st.end());
if(q==INF) q=*++st.begin();
p=re[p]; q=re[q];
t=t-Sum(p,q)+Sum(p,x)+Sum(x,q);
st.insert(dfn[x]);
}
else{
vis[x]=0;
s=st.size();
if(s==2) { st.erase(dfn[x]); printf("0\n"); continue; }
p=*--st.lower_bound(dfn[x]); q=*st.upper_bound(dfn[x]);
if(p==-INF) p=*--(--st.end());
if(q==INF) q=*++st.begin();
p=re[p]; q=re[q];
t=t+Sum(p,q)-Sum(p,x)-Sum(x,q);
st.erase(dfn[x]);
}
printf("%lld\n",t);
}
return 0;
}
[bzoj3991] [洛谷P3320] [SDOI2015] 寻宝游戏的更多相关文章
- [洛谷P3320] SDOI2015 寻宝游戏
问题描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的 ...
- 洛谷 P3320 [SDOI2015]寻宝游戏
因为寻宝路径是一个环,所以寻宝花费的最小时间与起点无关.宝应当按照所有宝藏所在位置的 dfs 序进行才能够使得花费的时间最短.设 \(dist_i\) 表示 \(i\) 到树根的最短距离,那么树上任意 ...
- 洛谷3320 SDOI2015寻宝游戏(set+dfs序)(反向迭代器的注意事项!)
被\(STL\)坑害了一个晚上,真的菜的没救了啊. 准确的说是一个叫\(reverse\ iterator\)的东西,就是我们经常用的\(rbegin()\) 有一个非常重要的性质 在反向迭代器中,+ ...
- P3320 [SDOI2015]寻宝游戏 解题报告
P3320 [SDOI2015]寻宝游戏 题目描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有\(N\)个村庄和\(N-1\)条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以 ...
- P3320 [SDOI2015]寻宝游戏
题目 P3320 [SDOI2015]寻宝游戏 做法 很巧妙的一种思路,懂了之后觉得大水题 首先要知道:在一棵树上标记一些点,然后从任意一点出发,遍历所有的的最小路径为\(dfs\)序从小到大遍历 那 ...
- Luogu P3320 [SDOI2015]寻宝游戏 / 异象石 【LCA/set】
期末考试结束祭! 在期末考试前最后一发的测试中,异象石作为第二道题目出现QAQ.虽然知道是LCA图论,但还是敲不出来QAQ. 花了两天竞赛课的时间搞懂(逃 异象石(stone.pas/c/cpp)题目 ...
- luogu P3320 [SDOI2015]寻宝游戏
大意:给定树, 要求维护一个集合, 支持增删点, 询问从集合中任取一点作为起点, 遍历完其他点后原路返回的最短长度. 集合中的点按$dfs$序排列后, 最短距离就为$dis(s_1,s_2)+...+ ...
- [BZOJ3991][SDOI2015]寻宝游戏
[BZOJ3991][SDOI2015]寻宝游戏 试题描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择 ...
- 【BZOJ3991】[SDOI2015]寻宝游戏 树链的并+set
[BZOJ3991][SDOI2015]寻宝游戏 Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩 ...
随机推荐
- NuGet 如何设置图标
在找 NuGet 的时候可以看到有趣的库都有有趣的图标,那么如何设置一个 NuGet 的图标 在开始之前,请在nuget官方网站下载 NuGet.exe 同时设置环境变量 环境变量设置的方法就是将 N ...
- dotnet 如何调试某个文件是哪个代码创建
我发现了自己的软件,会在桌面创建一个 1.txt 文件,但是我不知道是哪个代码创建的,那么如何进行快速的调试找到是哪个代码创建的 最简单的方法是使用 VisualStudio 全局搜 1.txt 看是 ...
- Xamarin 的一些资源汇总
https://github.com/xamarin/xamarin-forms-sampleshttps://github.com/EgorBo/CrossChat-Xamarin.Formshtt ...
- mybatis 整合redis作为二级缓存
核心关键在于定义一个RedisCache实现mytis实现的Cache接口 ** * @author tele * @Description RedisCache由于需要传入id, 由mybatis进 ...
- lombok工作原理分析
在Lombok使用的过程中,只需要添加相应的注解,无需再为此写任何代码.但是自动生成的代码到底是如何产生的呢? 核心之处就是对于注解的解析上.JDK5引入了注解的同时,也提供了两种解析方式. 运行时解 ...
- no supported authentication methods avaiable
在git(小乌龟)向github远程推送(push)文件是会报一个异常 no supported authentication methods avaiable 解决方法:因为git(小乌龟)和Git ...
- 使用git pull拉取代码的时候,无法拉取最新代码,报"unable to update local ref"错误。
使用git pull拉取代码的时候,无法拉取最新代码,报"unable to update local ref"错误. 除了重新clone一份代码外,还可以使用如下解决方案: .切 ...
- 远程管理服务器--批量管理服务器,vps
一般大型的企事业单位都有自己的服务器,但是服务器一般都放在机房,辐射较大,噪音大,如何能有效的避免这一情况呢?哈哈,那就来个远程桌面,远程操作服务器吧. 一.使用 iis7远程连接管理工具工具下载官网 ...
- Windows Live Writer 语法高亮
1.WindowsLiveWriter.CNBlogs.CodeHighlighter.rar 这个插件生成的高亮代码与网页上的一模一样,插入后即可立即显示效果,不过貌似它必须联网才能实时显示效果,因 ...
- 【TCP/IP网络编程】:09套接字的多种可选项
本篇文章主要介绍了套接字的几个常用配置选项,包括SO_SNDBUF & SO_RCVBUF.SO_REUSEADDR及TCP_NODELAY等. 套接字可选项和I/O缓冲大小 前文关于套接字的 ...