题目传送门:LOJ #2182

题意简述:

一棵 \(n\) 个节点的树,边有边权。

每个点可能是关键点,每次操作改变一个点是否是关键点。

求所有关键点形成的极小联通子树的边权和的两倍。

题解:

有一个结论:DFS 序求出后,假设关键点按照 DFS 序排序后是 \(\{a_1,a_2,\ldots ,a_k\}\)。

那么所有关键点形成的极小联通子树的边权和的两倍等于 \(\mathrm{dist}(a_1,a_2)+\mathrm{dist}(a_2,a_3)+\cdots+\mathrm{dist}(a_{k-1},a_k)+\mathrm{dist}(a_k,a_1)\)。

画个图感性理解一下,应该是很好懂的。

那么求一下 DFS 序,每次操作相当于往集合里加入/删除一个元素。

假设插入 \(x\),它DFS序左右两边分别是 \(y\) 和 \(z\)。那么答案加上 \(\mathrm{dist}(x,y)+\mathrm{dist}(x,z)-\mathrm{dist}(y,z)\) 即可。

删除同理。还有,求 LCA 就用个倍增或者树剖吧,Tarjan 离线比较麻烦。

用 STL 自带的 set 容器维护起来很方便。你也可以手写树状数组/线段树/平衡树。

#include <cstdio>
#include <set> typedef long long LL;
const int MN = 100005; int N, M;
int h[MN], nxt[MN * 2], to[MN * 2], w[MN * 2], tot;
inline void ins(int x, int y, int z) {
nxt[++tot] = h[x], to[tot] = y, w[tot] = z, h[x] = tot;
} int dfn[MN], idf[MN], dfc;
int dep[MN], faz[MN][17];
LL dis[MN]; void DFS(int u, int fz) {
dfn[u] = ++dfc; idf[dfc] = u; dep[u] = dep[faz[u][0] = fz] + 1;
for (int j = 1; 1 << j < dep[u]; ++j) faz[u][j] = faz[faz[u][j - 1]][j - 1];
for (int i = h[u]; i; i = nxt[i]) if (to[i] != fz) dis[to[i]] = dis[u] + w[i], DFS(to[i], u);
} inline int lca(int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
for (int d = dep[x] - dep[y], j = 0; d; d >>= 1, ++j)
if (d & 1) x = faz[x][j];
if (x == y) return x;
for (int j = 16; ~j; --j) if (faz[x][j] != faz[y][j])
x = faz[x][j], y = faz[y][j];
return faz[x][0];
} inline LL dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[lca(x, y)]; } bool vis[MN];
std::set<int> st;
std::set<int>::iterator it;
LL Ans; int main() {
scanf("%d%d", &N, &M);
for (int i = 1, x, y, z; i < N; ++i) {
scanf("%d%d%d", &x, &y, &z);
ins(x, y, z), ins(y, x, z);
}
DFS(1, 0);
for (int m = 1, x, y, z; m <= M; ++m) {
scanf("%d", &x);
x = dfn[x];
if (!vis[idf[x]]) st.insert(x);
y = idf[(it = st.lower_bound(x)) == st.begin() ? *--st.end() : *--it];
z = idf[(it = st.upper_bound(x)) == st.end() ? *st.begin() : *it];
if (vis[idf[x]]) st.erase(x);
x = idf[x];
LL d = dist(x, y) + dist(x, z) - dist(y, z);
if (!vis[x]) vis[x] = 1, Ans += d;
else vis[x] = 0, Ans -= d;
printf("%lld\n", Ans);
}
return 0;
}

洛谷 P3320: bzoj 3991: LOJ 2182: [SDOI2015]寻宝游戏的更多相关文章

  1. 洛谷 P5206: bzoj 5475: LOJ 2983: [WC2019] 数树

    一道技巧性非常强的计数题,历年WC出得最好(同时可能是比较简单)的题目之一. 题目传送门:洛谷P5206. 题意简述: 给定 \(n, y\). 一张图有 \(|V| = n\) 个点.对于两棵树 \ ...

  2. 洛谷 P1129 BZOJ 1059 cogs 660 [ZJOI2007]矩阵游戏

    题目描述 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏.矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的).每次可以对该矩阵进行两种操作: 行交 ...

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

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

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

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

  5. 3991: [SDOI2015]寻宝游戏

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

  6. P3320 [SDOI2015]寻宝游戏

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

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

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

  8. CH#56C 异象石 和 BZOJ3991 [SDOI2015]寻宝游戏

    异象石 CH Round #56 - 国庆节欢乐赛 描述 Adera是Microsoft应用商店中的一款解谜游戏. 异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图.这张地图上 ...

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

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

随机推荐

  1. Leetcode题库——39.组合总和

    @author: ZZQ @software: PyCharm @file: combinationSum.py @time: 2018/11/14 18:23 要求:给定一个无重复元素的数组 can ...

  2. JAVA 操作系统已经来到第五个版本了 现陆续放出三个版本 这是第二个版本

    package System2; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import ...

  3. Java入门第二季第2章封装

    什么是 Java 中的内部类 问:什么是内部类呢? 答:内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. 问:那为什么要将一个类定义在另一个类里 ...

  4. vm15安装esxi6.0

    vmware 15安装esxi6.0时发现出现没有硬盘选择,导致无法安装 在vm12上安装正常 经过测试 1.需要在虚拟机硬件兼容性上选择12.x 2.版本也要选6.0,不要选6.X 其次,esxi要 ...

  5. [问题排查]记录一次两个dubbo提供者同时在线,代码不一致导致问题的排查记录

    1. 需求 有一个需求job定时5秒一次,job[消费者]调用dsc[提供者]提供的dubbo完成:先清空redis的某个key,然后再往redis中放入新的数据,这是一个定时任务,需要每隔5秒执行一 ...

  6. android管理SD卡 mksdcard

    在创建Android模拟器的时候,会创建一个虚拟的sd卡.我们还可以通过mksdcard命令创建sd卡,在运行模拟器的时候,可以选择具体的sd卡. 1.创建sd卡: mksdcard [-l labl ...

  7. 【题解】 [ZJOI2009]假期的宿舍 (二分图匹配)

    懒得复制题面,戳我 Solution: 处理出床位.要留校的人(注意来访问的人一定住校),和人与人的关系(连边) 再接着就是二分图. 注意的就是连向的人必须是有床位的 还要注意的就是只用判断住校的同学 ...

  8. 【转】如何快速识别应用MOS管,几张图片就搞定了

    三极管是流控型器件,MOS管是压控型器件,两者存在相似之处.三极管机可能经常用,但MOS管你用的可能较少.对于MOS管先抛出几个问题: 如何区分P-MOS和N-MOS: 如何区分MOS的G.D.S管脚 ...

  9. 踩坑记(1)——使用slf4j+logback记录日志

    刚开始的jar包版本如下,因为选择jar包版本不同导致的一些坑,踩过了就记录下来: <spring.version>3.1.0.RELEASE</spring.version> ...

  10. 【HDU5687】Trie

    题目大意:需要维护一个支持以下操作的数据结构:(1)支持插入一个字符串(2)支持删除所有前缀等于给定字符串的单词(3)查询该数据结构中是否存在一个以给定字符串为前缀的字符串 题解:由题目可知,需要维护 ...