[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条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩 ...
随机推荐
- 1119 机器人走方格 V2 (组合数学)
M * N的方格,一个机器人从左上走到右下,只能向右或向下走.有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果. Input 第1行,2个数M,N,中间用空格隔开 ...
- javascript基础之循环
//while循环 <script type="text/javascript"> i = 1; while (i <= 6) { document.write( ...
- 如何用python“优雅的”调用有道翻译?
前言 其实在以前就盯上有道翻译了的,但是由于时间问题一直没有研究(我的骚操作还在后面,记得关注),本文主要讲解如何用python调用有道翻译,讲解这个爬虫与有道翻译的js“斗争”的过程! 当然,本文仅 ...
- 【GYM101409】2010-2011 ACM-ICPC, NEERC, Western Subregional Contest
A-Area and Circumference 题目大意:在平面上给出$N$个三角形,问周长和面积比的最大值. #include <iostream> #include <algo ...
- 【2016常州一中夏令营Day4】
小 W 走迷宫[问题描述]小 W 被小 M 困在了一个方格矩阵迷宫里,矩阵边界在无穷远处,我们做出如下的假设:a. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上:b. 走过的格子立即塌陷无 ...
- 学习Java第二周
这是学习java的第二周,又这样不知不觉的结束了 上周想要学习的这一周也都做到了,可是觉得进度有些慢了,学习了: 1. 接口和抽象类: 2. 集合与数组: 3. 方法的定义: 4. 递归算法: 5.对 ...
- Mybatis 多对多(易百教程)
mybatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作.因此在进行实体类多对多映射表 ...
- [Python之路] 内存管理&垃圾回收
一.python源码 1.准备源码 下载Python源码:https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz 解压得到文件夹: 我们主要关 ...
- eclipse中如何配置tomcat
1.打开eclipse上面的Windows选项,选择Preferences==>Server==>Runtime Environments==>Add 2.选择你电脑中安装的tomc ...
- 博帝飚速盘 16G
设备制造商: Patriot Memory当前协议 : USB2.0输入电流 : 300mA 芯片制造商: 群联(Phison)芯片型号 : PS2251-38闪存颗粒 : 美光( ...