一开始还真没想到。

发现从所有有宝藏的点出发绕一圈只要不刻意绕路答案都是一样的,即我们呢要求的最后答案$ans = dis(x_1, x_2) + dis(x_2, x_3) +... + dis(x_{k - 1}, x_k) + dis(x_k, x_1)$。

不刻意绕远路怎么办呢,我们把有宝藏的点按照$dfs$序,维护一个有序的$set$就可以了。

每次插入就相当于找到一个点$x$在$dfs$序中的前一个点$lst$和后一个点$nxt$,使最后的答案减去$dis(nxt, lst)$并加上$dis(x, nxt) + dis(lst, x)$,删除同理。

我选择用倍增来求$dis(x, y)$。

$set$的细节一开始没想清楚,写了一会儿。

时间复杂度$O(nlogn)$。

Code:

#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
typedef long long ll; const int N = 1e5 + ;
const int Lg = ; int n, qn, tot = , head[N];
int dep[N], fa[N][Lg], dfsc = , id[N], mp[N];
ll ans = 0LL, dis[N];
bool ex[N];
set <int> s; struct Edge {
int to, nxt;
ll val;
} e[N << ]; inline void add(int from, int to, ll val) {
e[++tot].to = to;
e[tot].val = val;
e[tot].nxt = head[from];
head[from] = tot;
} template <typename T>
inline void read(T &X) {
X = ; char ch = ; T op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} void dfs(int x, int fat, int depth, ll nowDis) {
fa[x][] = fat, dep[x] = depth, dis[x] = nowDis;
id[x] = ++dfsc, mp[id[x]] = x;
for(int i = ; i <= ; i++)
fa[x][i] = fa[fa[x][i - ]][i - ];
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs(y, x, depth + , nowDis + e[i].val);
}
} inline void swap(int &x, int &y) {
int t = x; x = y; y = t;
} inline int getLca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = ; i >= ; i--)
if(dep[fa[x][i]] >= dep[y])
x = fa[x][i];
if(x == y) return x;
for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][];
} inline ll getDis(int x, int y) {
int z = getLca(x, y);
return dis[x] + dis[y] - * dis[z];
} int main() {
read(n), read(qn);
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
ll v; read(v);
add(x, y, v), add(y, x, v);
}
dfs(, , , 0LL); /* for(int i = 1; i <= n; i++)
printf("%d ", id[i]);
printf("\n"); */ for(int x, cnt = ; qn--; ) {
read(x);
if(!ex[x]) {
ex[x] = ;
s.insert(id[x]);
++cnt;
if(cnt == ) {
puts("");
continue;
}
set <int> :: iterator it = s.find(id[x]), it2 = it;
if(it == s.begin()) it = s.end();
int lst = *(--it);
it = s.find(id[x]);
int nxt = ;
if((++it2) == s.end()) nxt = *(s.begin());
else nxt = *(++it);
ans += getDis(mp[lst], x) + getDis(mp[nxt], x) - getDis(mp[lst], mp[nxt]);
} else {
ex[x] = ;
--cnt;
if(cnt == ) {
puts("");
s.erase(id[x]);
continue;
}
set <int> :: iterator it = s.find(id[x]), it2 = it;
if(it == s.begin()) it = s.end();
int lst = *(--it);
it = s.find(id[x]);
int nxt = ;
if((++it2) == s.end()) nxt = *(s.begin());
else nxt = *(++it);
ans -= getDis(mp[lst], x) + getDis(mp[nxt], x) - getDis(mp[lst], mp[nxt]);
s.erase(id[x]);
}
printf("%lld\n", ans);
} return ;
}

Luogu 3320 [SDOI2015]寻宝游戏的更多相关文章

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

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

  2. luogu P3320 [SDOI2015]寻宝游戏

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

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

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

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

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

  5. bzoj 3991: [SDOI2015]寻宝游戏 虚树 set

    目录 题目链接 题解 代码 题目链接 bzoj 3991: [SDOI2015]寻宝游戏 题解 发现每次答案就是把虚树上的路径*2 接在同一关键点上的点的dfs序是相邻的 那么用set动态维护dfs序 ...

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

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

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

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

  8. 3991: [SDOI2015]寻宝游戏

    3991: [SDOI2015]寻宝游戏 https://www.lydsy.com/JudgeOnline/problem.php?id=3991 分析: 虚树+set. 要求树上许多点之间的路径的 ...

  9. 【BZOJ3991】[SDOI2015]寻宝游戏 树链的并+set

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

随机推荐

  1. linux通过脚本获取内存信息

    1 原理 脚本中通过执行free获取内存信息,然后将文本信息通过“空格”分隔符分割成字符串数组将不同信息提取出来,最后通过bc计算出百分比 2 脚本 #!/bin/shHOSTNAME=`hostna ...

  2. delphi各个版本编译开关值

    delphi各个版本编译开关值 {$IFDEF VER80}  - Delphi 1{$IFDEF VER90}  - Delphi 2{$IFDEF VER100} - Delphi 3{$IFDE ...

  3. css 采集下载

    软件应用范围: 看到喜欢的网页,另存为的话,并不能直接保存css中引用的图片. 那么就有了本软件的用武之地. 亮点:自动匹配文件内的相对路径.css内图片地址值md5保存,用来避免不同文件夹同名文件的 ...

  4. spring boot: EL和资源 (一般注入说明(二) @Service注解 @Component注解)

    @Service用于标注业务层组件 : 将当前类注册为spring的Bean @Controller用于标注控制层组件(如struts中的action) @Repository用于标注数据访问组件,即 ...

  5. 纯css实现3D字体

    下面分别是html,css和js代码: <div class="wrapper"> <h1 contenteditable data-heading=" ...

  6. C#异步编程之浅谈Task

    上一篇讲到了.Net4.5新增的async和await关键字,其实async和await算是一组标记,真正实现异步操作的是Task新开的任务线程. 什么是Task Task是.Net4.0新增用来处理 ...

  7. SSH Secure Shell 的使用

    一,安装Secure Shell ,之后会出现SSH Secure Shell client 和SSH Secure File Transfer client 两个快捷方式. 需要服务器的IP地址,用 ...

  8. Android: 一个两点触控的案例

    下面是一个两点触控的案例代码: package com.zzj; import android.app.Activity; import android.os.Bundle; import andro ...

  9. 2017-2018-1 20179203 《Linux内核原理与分析》第五周作业

    攥写人:李鹏举 学号:20179203 ( 原创作品转载请注明出处) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/US ...

  10. [BZOJ2962][清华集训]序列操作

    bzoj luogu 题意 有一个长度为\(n\) 的序列,有三个操作: \(I \ \ a\ b\ c\ :\)表示将\([a,b]\)这一段区间的元素集体增加\(c\): \(R \ \ a\ b ...