「SDOI2015」寻宝游戏

传送门
Luogu

解题思路

发现一个性质:
对于所有的宝藏点 \({a_1,a_2...a_k}\) ,按照dfs序递增排列,答案就是:
\(dis(a_1, a_2) + dis(a_2, a_3) + \cdots + dis(a_{k-1}, a_k) + dis(a_k, a_1)\)
考虑加入一个点的贡献:
假设加入的点是 \(u\),那么贡献就是 \(dis(L, u) + dis(R, u) - dis(L, R)\)
其中 \(L, R\) 分别是 \(u\) 点的对应dfs序的前驱和后继。
这个应该挺好理解的。
然后我们搞一个 set 就可以维护了。

细节注意事项

  • 咕咕咕

参考代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#include <set>
#define rg register
using namespace std;
template < typename T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while (!isdigit(c)) f |= (c == '-'), c = getchar();
    while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
    s = f ? -s : s;
}

typedef long long LL;
const int _ = 100010;

int tot, head[_], nxt[_ << 1], ver[_ << 1], w[_ << 1];
inline void Add_edge(int u, int v, int d)
{ nxt[++tot] = head[u], head[u] = tot, ver[tot] = v, w[tot] = d; }

int n, m, num, vis[_]; LL dis[_];
int fa[_], siz[_], dep[_], top[_], son[_], dfn[_], id[_];
set < int > s;
set < int > ::iterator it;

inline void dfs1(int u, int f) {
    fa[u] = f, dep[u] = dep[f] + 1;
    siz[u] = 1, id[dfn[u] = ++num] = u;
    for (rg int v, i = head[u]; i; i = nxt[i])
        if (!dep[v = ver[i]]) {
            dis[v] = dis[u] + w[i];
            dfs1(v, u), siz[u] += siz[v];
            if (siz[son[u]] < siz[v]) son[u] = v;
        }
}

inline void dfs2(int u, int topf) {
    top[u] = topf;
    if (!son[u]) return; dfs2(son[u], topf);
    for (rg int v, i = head[u]; i; i = nxt[i])
        if (!top[v = ver[i]]) dfs2(v, v);
}

inline int LCA(int x, int y) {
    int fx = top[x], fy = top[y];
    while (fx != fy) {
        if (dep[fx] < dep[fy]) swap(x, y), swap(fx, fy);
        x = fa[fx], fx = top[x];
    }
    return dep[x] < dep[y] ? x : y;
}

inline LL dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[LCA(x, y)]; }

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.in", "r", stdin);
#endif
    read(n), read(m);
    for (rg int u, v, d, i = 1; i < n; ++i)
        read(u), read(v), read(d), Add_edge(u ,v, d), Add_edge(v, u, d);
    dfs1(1, 0), dfs2(1, 1);
    LL ans = 0;
    while (m--) {
        int x, d; read(x), d = dfn[x];
        if (!vis[x]) s.insert(d);
        int l = id[(it = s.lower_bound(d)) == s.begin() ? *--s.end() : *--it];
        int r = id[(it = s.upper_bound(d)) == s.end() ? *s.begin() : *it];
        if (vis[x]) s.erase(d);
        LL delta = dist(l, x) + dist(r, x) - dist(l, r);
        if (vis[x]) ans -= delta, vis[x] = 0;
        else ans += delta, vis[x] = 1;
        printf("%lld\n",  ans);
    }
    return 0;
}

完结撒花 \(qwq\)

原文地址:https://www.cnblogs.com/zsbzsb/p/11746534.html

时间: 2024-11-05 18:54:37

「SDOI2015」寻宝游戏的相关文章

loj2004. 「SDOI2017」硬币游戏

2004. 「SDOI2017」硬币游戏 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况. 用 $ \texttt{H} $ 表示正面朝上, 用 $ \texttt{T} $ 表示反面朝上,扔很多次硬币后,会得到一个硬币序列.比如 $ \texttt{HTT} $ 表示第一次正面朝上,后两次反面朝上. 但扔到什么时

[SDOI2015][BZOJ3991] 寻宝游戏|set|dfs序|虚树|树上倍增LCA

3991: [SDOI2015]寻宝游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 427  Solved: 212[Submit][Status][Discuss] Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直

【BZOJ】【3991】【SDOI2015】寻宝游戏

dfs序 我哭啊……这题在考试的时候(我不是山东的,CH大法吼)没想出来……只写了50分的暴力QAQ 而且苦逼的写的比正解还长……我骗点分容易吗QAQ 骗分做法: 1.$n,m\leq 1000$: 直接找一个关键点做根进行深搜,算出其他关键点都与root连通的最小边权和,再×2 2.一条链的情况:用set维护序列中哪些点选了,然后ans=(sum[tail]-sum[head])*2; 其中sum[i]表示从序列首到第 i 个的边长之和……嗯就是前缀和优化一下= =取首和尾这一段的Len之和

「Githug」Git 游戏通关流程

Githug 他喵的这是个啥!?难道不是 GitHub 拼错了么,和 Git 什么关系? 和游戏又有什么关系? 其实,他的元身在这里:https://github.com/Gazler/githug ,这个命令行工具被设计来练习你的 Git 技能,它把平常可能遇到的一些场景都实例化,变成一个一个的关卡,一共有 55 个关卡,所以将他形象的形容为 Git 游戏. 既然是游戏,作为一个专业的游戏玩家,通关自然是我的最终目标了!!! 安装游戏 没什么好说的,终端运行如下命令即可,如果碰到了墙,自行搬梯

@loj - [email&#160;protected] 「SDOI2017」硬币游戏

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况. 用 H 表示正面朝上, 用 T 表示反面朝上,扔很多

「SCOI2010」连续攻击游戏

传送门 Luogu 解题思路 二分图匹配,关键是建图. 如果我们naive地直接把每个武器的两个属性分为两部建图的话,显然是跑不了的. 我们考虑把每一个武器的属性向它连边:\(a_i \rightarrow i,b_i \rightarrow i\) 然后我们从属性这一部从小到大枚举,寻找增广路,直到找不到就输出答案. 细节注意事项 咕咕咕 参考代码 #include <algorithm> #include <iostream> #include <cstring>

「SDOI2015」序列统计

题目链接:Click here Solution: 容易得到这样一个\(dp\),设\(f[i][j]\)表示已经选了\(i\)个数,乘积\(mod \,\,m\)后为\(j\)的方案 \[ f[2\times i][j]=\sum_{a\times b\equiv j\,\,(mod\,\, m)} f[i][a]\times f[i][b] \] 考虑如何优化这个转移方程,注意到这道题的模数为\(1004535809\),这是一个很大的提示,考虑\(NTT\) 但是我们知道\(NTT\)处理

「一本通 4.4 例 3」异象石 与 [SDOI2015]寻宝游戏

这两个题差不多先说异象石把 主要是找到本题规律,将所加入的点按dfs序排序,记录为a[1],a[2]..a[n]则当前的答案为每个点与前一个点的距离(第一个点则与最后一点) 当然要动态维护答案,每加入一个点就+与前驱的距离+与后驱的距离-前驱与后驱的距离(删点的话ans减去这个值就好 不过异象石最后的答案要/2: 至于维护的话用set就好 1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 cons

LibreOJ #6191. 「美团 CodeM 复赛」配对游戏

二次联通门 : LibreOJ #6191. 「美团 CodeM 复赛」配对游戏 /* LibreOJ #6191. 「美团 CodeM 复赛」配对游戏 概率dp 不是很懂为什么这样做... */ #include <cstdio> #include <iostream> const int BUF = 12312312; char Buf[BUF], *buf = Buf; inline void read (int &now) { for (now = 0; !isdi