[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条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩 ...
随机推荐
- 有状态无状态回话bean
1.有状态(Stateful) 可以在不同的方法调用间保持针对各个客户端的状态 与客户端的联系必须被维持,这样做开销要大一些 有状态也可以这样理解,它存在存储能力,也就是说至少有一个属性来标识它目前的 ...
- LightOJ - 1287 Where to Run (期望dp+记忆化)
题面: Last night you robbed a bank but couldn't escape and when you just got outside today, the police ...
- 使用FluentEmail发送outlook邮件
一,邮箱账号相关设置 1,创建outLook邮箱. 2,进入邮箱设置->同步电子邮件->允许设备和应用使用pop 3,设置microsoft账号的应用程序密码->进入安全性页面-&g ...
- C++Review6_优先队列priority_queue
普通队列是一个先进先出的数据结构,元素在队尾添加,在队头删除. 优先队列的出队逻辑相比于普通队列发生了改变,具有最高优先级的元素先出队. 在C++中只要包含了#include<queue> ...
- LabWindows/CVI基础
1.LabWindows/CVI了解 提到NI公司,大家可能最先联想到的是NI公司推出的LabVIEW软件.LabWindows/CVI与LabVIEW相比,主要应用在各种测试.控制.故障分析及信息处 ...
- Redis事务、持久化、发布订阅
一.Redis事物 1. 概念 Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证: 事务是一个单独的隔离操作:事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中,不会被其他 ...
- Excel解除'工作表保护密码',并复原密码设定
前提要求 Office 2003(也就是老版的.xls文件) 用到宏操作 可以解除[审阅->保护工作表]的密码保护,其他的密码保护不能处理. 解决方案 打开需解除保护密码的Excel文件(.xl ...
- 洛谷$P$2286 宠物收养场 $[HNOI2004]$ $splay$
正解:$splay$ 解题报告: 传送门! $splay$板子,,,? 先考虑这题要实现些什么东西嘛$QwQ$ 其实只要实现一个东西?就查询数列中与给定数字相差最小的数,显然用$splay$查询前驱后 ...
- 洛谷P1832 A+B Problem(再升级) 题解 完全背包方案计数
题目链接:https://www.luogu.com.cn/problem/P1832 题目大意: 给定一个正整数n,求将其分解成若干个素数之和的方案总数. 解题思路: 首先找到所有 \(\le n\ ...
- 1081 检查密码 (15分)C语言
本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能.该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母.数字和小数点 .,还必须既有字母也有数字. 输入格式: 输入第一行 ...