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

posted @ 2025-06-28 20:04  NeeDna  阅读(15)  评论(0)    收藏  举报