【题解】P2056 [ZJOI2007] 捉迷藏
题意
动态维护树上点集直径。
思路
线段树。
这个题纯粹就是刻板印象检验,结果把我区分了 /xk
谁规定线段树只能维护连续区间?
首先有直径结论:设树上点集 \(S\) 的直径点集为 \(F(S)\),则 \(F(S \cup T) \subset F(S) \cup F(T)\)
于是直径点集具有可合并性,考虑用线段树维护区间直径点集。
合并信息的时候直接分类讨论当前区间的直径端点。
时间复杂度 \(O(n \log n + m \log^2 n)\)
代码
#include <iostream>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 5;
const int t_sz = maxn << 2;
int n, q;
int fa[maxn], son[maxn], top[maxn], sz[maxn], dep[maxn];
vector<int> g[maxn];
void dfs1(int u, int f)
{
fa[u] = f, dep[u] = dep[f] + 1, sz[u] = 1;
for (int v : g[u])
{
if (v == f) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t)
{
top[u] = t;
if (son[u]) dfs2(son[u], t);
for (int v : g[u])
{
if ((v == fa[u]) || (v == son[u])) continue;
dfs2(v, v);
}
}
int lca(int u, int v)
{
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return (dep[u] < dep[v] ? u : v);
}
int dis(int u, int v) { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; }
namespace SGT
{
int u[t_sz], v[t_sz], w[t_sz];
void get_max(int k, int _u, int _v, int _w) { if (_w > w[k]) u[k] = _u, v[k] = _v, w[k] = _w; }
void push_up(int k)
{
int ls = (k << 1), rs = (k << 1 | 1);
w[k] = -1;
get_max(k, u[ls], v[ls], w[ls]);
get_max(k, u[rs], v[rs], w[rs]);
if ((w[ls] == -1) || (w[rs] == -1)) return;
get_max(k, u[ls], u[rs], dis(u[ls], u[rs]));
get_max(k, u[ls], v[rs], dis(u[ls], v[rs]));
get_max(k, v[ls], u[rs], dis(v[ls], u[rs]));
get_max(k, v[ls], v[rs], dis(v[ls], v[rs]));
}
void build(int k, int l, int r)
{
if (l == r)
{
u[k] = v[k] = l, w[k] = 0;
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
push_up(k);
}
void update(int k, int l, int r, int p)
{
if (l == r)
{
if (w[k] == -1) u[k] = v[k] = l, w[k] = 0;
else u[k] = v[k] = 0, w[k] = -1;
return;
}
int mid = (l + r) >> 1;
if (p <= mid) update(k << 1, l, mid, p);
else update(k << 1 | 1, mid + 1, r, p);
push_up(k);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1, u, v; i <= n - 1; i++)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 1);
SGT::build(1, 1, n);
cin >> q;
while (q--)
{
char opt;
cin >> opt;
if (opt == 'C')
{
int u;
cin >> u;
SGT::update(1, 1, n, u);
}
else printf("%d\n", SGT::w[1]);
}
return 0;
}