P3320 [SDOI2015] 寻宝游戏
P3320\(\mathbf{} \begin{Bmatrix} \frac{{\Large LUOGU-P3320} }{{\color{Red}\Large Solution} }\mathbf{} {No.25} \end{Bmatrix}\times{}\) NeeDna
题意:
有一棵树,有一部分动态的关键点,求从树的一点遍历所有关键点然后回到自己的最小代价,每个点可能是关键点,每次操作改变一个点是否是关键点。
分析:
发现路径肯定为一个环,所以从路径上任意一点出发,回到自己都是一样的。
那么我们考虑什么时候路径最短,若用dfs序,有一个结论是从小到大依次经过最优。
证明:
我不太会严格证明,但是可以看下。首先可以手搓几个数据,问题可以转变为把所有关键点连起来的和的两倍,接下来要路径最小,对于任意两个点,都会在 \(lca(x,y)\) 上合并,越早合并越好,恰好满足dfsx序的性质,即相邻的两点lca是最深的,所以最早合并,因此我们可以写出路径长度: \(\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 容器维护起来很方便。你也可以手写树状数组/线段树/平衡树。
如果用树状数组就用权值的,开两个分别维护前缀后缀即可,比较ez。
AC code:
#include <bits/stdc++.h>
#define lb lower_bound
#define ub upper_bound
using namespace std;
typedef long long ll;
const int N = 100005, M = (N << 1), K = 21;
int n, q, ecnt = 0, idx, to[M], nxt[M], fir[N], depth[N], fa[N][K], dfn[N], pos[N];
ll dis[N], len[M], ans; bool vis[N]; set <int> S;
template <class T> void rd(T &x) {
char c = getchar(); int f = 1; x = 0;
for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
void ae(int u, int v, ll w) {to[++ecnt] = v; len[ecnt] = w; nxt[ecnt] = fir[u]; fir[u] = ecnt;}
int lca(int u, int v) {
int i; if (depth[u] < depth[v]) swap(u, v);
for (i = 20; ~i; --i) if (depth[fa[u][i]] >= depth[v]) u = fa[u][i];
if (u == v) return u;
for (i = 20; ~i; --i) if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
ll calc(int u, int v) {return dis[u] + dis[v] - dis[lca(u, v)] * 2;}
void dfs(int u, int dep, int f) {
int i; dfn[u] = ++idx; pos[idx] = u; depth[u] = dep; fa[u][0] = f;
for (i = fir[u]; i; i = nxt[i]) {
int v = to[i], w = len[i];
if (v != f) dis[v] = dis[u] + w, dfs(v, dep + 1, u);
}
}
int main() {
int i, j; rd(n); rd(q);
for (i = 1; i < n; ++i) {
int u, v; ll w; rd(u); rd(v); rd(w);
ae(u, v, w); ae(v, u, w);
}
dfs(1, 1, 0); S.insert(-1); S.insert(999999);
for (i = 1; i <= 20; ++i)
for (j = 1; j <= n; ++j) fa[j][i] = fa[fa[j][i - 1]][i - 1];
for (i = 1; i <= q; ++i) {
int x, p, l, r; ll tmp = 0; rd(x);
if (!vis[x]) S.insert(dfn[x]), p = 1; else S.erase(dfn[x]), p = -1;
vis[x] ^= 1; l = *--S.lb(dfn[x]); r = *S.ub(dfn[x]);
if (l != -1) ans += calc(pos[l], x) * p; if (r != 999999) ans += calc(pos[r], x) * p;
if (l != -1 && r != 999999) ans -= calc(pos[l], pos[r]) * p;
if (S.size() != 2) tmp = calc(pos[*++S.begin()], pos[*--S.lb(999999)]);
printf("%lld\n", ans + tmp);
}
return 0;
}
code from muller

浙公网安备 33010602011771号