[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条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩 ...
随机推荐
- get_free_page 和其友
如果一个模块需要分配大块的内存, 它常常最好是使用一个面向页的技术. 请求整个页也 有其他的优点, 这个在 15 章介绍. 为分配页, 下列函数可用: get_zeroed_page(unsigned ...
- 2018-11-9-win2d-CanvasCommandList-使用方法
title author date CreateTime categories win2d CanvasCommandList 使用方法 lindexi 2018-11-9 20:8:4 +0800 ...
- 托管exe文件的加载和执行
托管exe文件被启动的时候,首先被PE Loader载入.PE Loader载入exe文件之后,会分析PE文件头的data directory table,如果CLR_Header内的值不为0,表示该 ...
- SSL/TLS 配置
Quick Start 下列说明将使用变量名 $CATALINA_BASE 来表示多数相对路径所基于的基本目录.如果没有为 Tomcat 多个实例设置 CATALINA_BASE 目录,则 $CATA ...
- MFC_对话框_访问控件_7种方法_A
访问对话框控件的七种方法 方法一. GetDlgItem()->GetWindowText(); GetDlgItem()->SetWindowText(); 方法二. GetDlgIte ...
- Django框架之中间件MiddleWare
Django中的中间件是一个轻量级.底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出.中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健 ...
- 阿里面试官让我讲讲Unicode,我讲了3秒说没了,面试官说你可真菜
本文首发于微信公众号:程序员乔戈里 乔哥:首先说说什么是Unicode.码点吧~要想搞懂,这些概念必须清楚 什么是Unicode? 下图来自http://www.unicode.org/standar ...
- ELK学习实验003:Elasticsearch 集群安装
前面已经介绍了Elasticsearch这个工具,并对单机安装做了简单介绍,现在使用三台机器做一个elasticsearch集群 一 环境准备 1.1 机器准备 1.2 同步时间 [root@node ...
- 洛谷$P$2518 计数 $[HAOI2010]$ 数位$dp$
正解:数位$dp$ 解题报告: 传送门$w$ 感觉省选的数位$dp$还是比较有质量的辣,,,至少有一定的思维难度是趴$QwQ$ 这题要考虑到一个,我认为比较关键的点,就,对于一个位数不满的数,可以理解 ...
- 【记录】.bin文件 到 .vdi文件的转换教程
.bin文件 到 .vdi文件的转换教程 1. 背景 想体验一下 Chrome OS 系统,于是准备在 虚拟机VirtualBox(Mac版) 中安装一下,网上教程非常少,找到如下教程 原贴地址:ht ...