问题描述

小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。

小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物

输入格式

第一行,两个整数N、M,其中M为宝物的变动次数。接下来的N-1行,每行三个整数x、y、z,表示村庄x、y之间有一条长度为z的道路。接下来的M行,每行一个整数t,表示一个宝物变动的操作。若该操作前村庄t内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。

输出格式

M行,每行一个整数,其中第i行的整数表示第i次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出0。

样例输入

4 5

1 2 30

2 3 50

2 4 60

2

3

4

2

1

样例输出

0

100

220

220

280

解析

手动模拟之后我们可以发现,对于一个已经确定哪些地方有宝藏的地图,无论从哪个地方出发的路径长度是一样的。稍加归纳我们可以发现,要求的答案就是

\[dis(p_1,p_2)+dis(p_2,p_3)+...+dis(p_{n-1},p_n)
\]

其中\(p\)是按照DFS序排序后的宝藏地点数组。因此,我们可以用set维护所有有宝藏的地点序列,关键字为DFS序。每次插入都找到插入点在set中的前驱后继,然后用LCA求路径长度修改答案即可。

代码

#include <iostream>
#include <cstdio>
#include <set>
#include <cmath>
#define int long long
#define N 100002
using namespace std;
int head[N],ver[N*2],nxt[N*2],edge[N*2],l;
int n,m,i,f[N][20],dep[N],dfn[N],dis[N],tim;
bool vis[N];
struct node{
int p;
node(int _p){p=_p;}
bool operator < (const node &a) const{
return dfn[a.p]<dfn[p];
}
};
set<node> s;
set<node>::iterator it1,it2;
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y,int z)
{
l++;
ver[l]=y;
edge[l]=z;
nxt[l]=head[x];
head[x]=l;
}
void dfs(int x,int pre)
{
dfn[x]=++tim;
f[x][0]=pre;
dep[x]=dep[pre]+1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y!=pre){
dis[y]=dis[x]+edge[i];
dfs(y,x);
}
}
}
void init()
{
dfs(1,0);
for(int j=0;(1<<(j+1))<=n;j++){
for(int i=1;i<=n;i++) f[i][j+1]=f[f[i][j]][j];
}
}
int LCA(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v);
int tmp=dep[v]-dep[u];
for(int i=0;(1<<i)<=tmp;i++){
if((1<<i)&tmp) v=f[v][i];
}
if(u==v) return u;
for(int i=log2(1.0*n);i>=0;i--){
if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
}
return f[u][0];
}
int dist(int x,int y)
{
return dis[x]+dis[y]-2*dis[LCA(x,y)];
}
signed main()
{
n=read();m=read();
for(i=1;i<n;i++){
int u=read(),v=read(),w=read();
insert(u,v,w);
insert(v,u,w);
}
init();
int ans=0;
for(i=1;i<=m;i++){
int x=read();
if(vis[x]){
vis[x]=0;
if(s.size()!=1){
it1=it2=s.find(node(x));
if(it1==s.begin()) it1=s.end();
it1--;it2++;
if(it2==s.end()) it2=s.begin();
ans+=dist((*it1).p,(*it2).p)-dist(x,(*it1).p)-dist(x,(*it2).p);
}
s.erase(node(x));
}
else{
vis[x]=1;
s.insert(node(x));
if(s.size()!=1){
it1=it2=s.find(node(x));
if(it1==s.begin()) it1=s.end();
it1--;it2++;
if(it2==s.end()) it2=s.begin();
ans-=dist((*it1).p,(*it2).p)-dist(x,(*it1).p)-dist(x,(*it2).p);
}
}
printf("%lld\n",ans);
}
return 0;
}

[洛谷P3320] SDOI2015 寻宝游戏的更多相关文章

  1. [bzoj3991] [洛谷P3320] [SDOI2015] 寻宝游戏

    Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有 \(N\) 个村庄和 \(N-1\) 条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬 ...

  2. 洛谷 P3320 [SDOI2015]寻宝游戏

    因为寻宝路径是一个环,所以寻宝花费的最小时间与起点无关.宝应当按照所有宝藏所在位置的 dfs 序进行才能够使得花费的时间最短.设 \(dist_i\) 表示 \(i\) 到树根的最短距离,那么树上任意 ...

  3. 洛谷3320 SDOI2015寻宝游戏(set+dfs序)(反向迭代器的注意事项!)

    被\(STL\)坑害了一个晚上,真的菜的没救了啊. 准确的说是一个叫\(reverse\ iterator\)的东西,就是我们经常用的\(rbegin()\) 有一个非常重要的性质 在反向迭代器中,+ ...

  4. P3320 [SDOI2015]寻宝游戏 解题报告

    P3320 [SDOI2015]寻宝游戏 题目描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有\(N\)个村庄和\(N-1\)条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以 ...

  5. P3320 [SDOI2015]寻宝游戏

    题目 P3320 [SDOI2015]寻宝游戏 做法 很巧妙的一种思路,懂了之后觉得大水题 首先要知道:在一棵树上标记一些点,然后从任意一点出发,遍历所有的的最小路径为\(dfs\)序从小到大遍历 那 ...

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

    期末考试结束祭! 在期末考试前最后一发的测试中,异象石作为第二道题目出现QAQ.虽然知道是LCA图论,但还是敲不出来QAQ. 花了两天竞赛课的时间搞懂(逃 异象石(stone.pas/c/cpp)题目 ...

  7. luogu P3320 [SDOI2015]寻宝游戏

    大意:给定树, 要求维护一个集合, 支持增删点, 询问从集合中任取一点作为起点, 遍历完其他点后原路返回的最短长度. 集合中的点按$dfs$序排列后, 最短距离就为$dis(s_1,s_2)+...+ ...

  8. 【LG3320】[SDOI2015]寻宝游戏

    [LG3320][SDOI2015]寻宝游戏 题面 洛谷 题解 不需要建虚树的虚树2333... 贪心地想一下,起始节点肯定是在关键点上,访问顺序就是\(dfs\)序. 那么对于每次询问, \[ An ...

  9. [BZOJ3991][SDOI2015]寻宝游戏

    [BZOJ3991][SDOI2015]寻宝游戏 试题描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择 ...

随机推荐

  1. java:Topic About(猴子吃桃,快速排序,选择排序,插入排序)

    1. 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个.以后每天早上都吃了前一天剩下的一半零一个.到第10天早上想再吃时,见只 ...

  2. C++ 多文件编译简述:头文件、链接性、声明与定义

    目录 Commen Sense 头文件 链接性 static 与链接性控制 extern 与外部链接性 Reference Commen Sense C++ 在编译时对每个翻译单元(Translati ...

  3. 【VS开发】字符串进制等转换关系及方法

    C语言提供了几个标准库函数,可以将任意类型(整型.长整型.浮点型等)的数字转换为字符串.以下是用itoa()函数将整数转换为字符串的一个例子:# include <stdio.h># in ...

  4. mysql——单表查询——其它整理示例01

    create database see; use database see; drop database sww; ========================================== ...

  5. IDEA github 上传项目, 拉取, 删除

    1.IDEA登录github账号 settings -> Version Controller -> GitHub 用户名密码登录 或token登录都可以 2.VCS -> impo ...

  6. Win10无法安装.net framework 3.5出错提示无法安装以下功能该怎么办?

    在Windows操作系统中,.NET Framewor对今天应用程序的成功提供了的安全解决方案,它能强化两个安全模型间的平衡.在提供对资源的访问,以便以完成有用的工作,对应用程序的安全性作细致的控制以 ...

  7. Spring(五)--autowire自动装配和spel

    autowire自动装配和spel 1.需要的实体类 2.需要的配置文件 <?xml version="1.0" encoding="UTF-8"?> ...

  8. Java 创建bat命令文件运行可执行jar包

    在可执行jar包所在文件夹下创建txt文件(必须在同一文件夹目录下),打开创建的txt文件输入如下内容并保存: @echo off java -jar 包名.jar pause 如下图所示: 然后将后 ...

  9. PythonDay11

    第十一章函数使用_迭代器 今日内容 函数名的第一类对象及使用 f-strings 迭代器 函数名的第一类对象及使用 函数名可以当做值,赋值给变量 函数名可以当做容器内的元素 函数名可以当做函数的参数 ...

  10. Win10下编译OpenJDK8

    导航目录 Win10下编译OpenJDK8 相关参考文章 编译环境 编译前准备 1.安装 Visual Studio 2010 Professional 2. 准备OpenJDK8 3. 编译JDK环 ...