BZOJ1095 [ZJOI2007] Hide 捉迷藏 (括號序列 + 線段樹)
題意
給你一顆有 \(n\) 個點的樹 , 共有 \(m\) 次操作 有兩種類別qwq
- 將樹上一個點染黑/白;
- 詢問樹上最遠的兩個黑點的距離.
\((n \le 200000, m ≤500000)\)
題解
樹上距離如果不帶權的話我們很容易用一個括號序列來維護qwq
進來的時候我們新增一個左括號 把這個數字放進來 出去的時候我們新增一個右括號
其實這個和尤拉序差不多
比如 這顆樹的括號序列就是 \((1(2)(3(4)(5(6(7))))(8))\)
然後有一個顯然的定理
對於樹上任意兩個點 , 它們之間的距離等於這兩個數字之間未匹配的括號數量
這個比較顯然 我們可以這樣考慮 兩個點到他們 \(\mathrm{LCA}\)
這是因為中間和上面的括號都已經全部匹配完了 然後距離就是它們加起來了
我們需要維護的就是樹上兩個黑點之間未匹配的括號數的最大值
大概都長這個樣子 \())))((((\)
我們考慮用線段樹維護這個東西
這個看起來比較難以維護 所以我們需要一些輔助的東西才能進行維護
接下來的定義 都要在去掉 匹配括號的條件 下進行!!!
定義 \(o\) 為線段樹上當前的節點 \(ls\) 為當前節點在的左兒子 \(rs\) 為右兒子
需要維護當前區間右括號 \(a\) 和左括號 \(b\) 的數量
然後我們有兩個顯然的轉移
\[a[o] = a[ls] + \max(a[rs] - b[ls], 0); \\ b[o] = b[rs] + \max(b[ls] - a[rs], 0);\]
然後我們需要維護另外四個東西 , 就是
從當前序列中一個黑點到序列兩端的未匹配括號和的最大值 和 差的最大值
\(rp=right \ plus\) 這個就是 這個區間內的一個黑點到它右端 右括號 \()\) 和 左括號 \((\) 加起來的最大值
\(rm = right \ minus\) 就是 這個區間內的一個黑點到它右端 右括號 \()\) 比 左括號 \((\) 多的數量的最大值
\(lp = left \ plus\) 這個同理代表 這個區間內的一個黑點到它左端 右括號 \()\) 和 左括號 \((\) 加起來的最大值
\(lm = left \ minus\) 這個區間內一個黑點到它左端 左括號 \((\)
然後我們就有如下的轉移咯qwq
自己思考一下它的意義\[rp[o] = max(rp[rs], max(rp[ls] - a[rs] + b[rs], rm[ls] + a[rs] + b[rs]));\]
\[rm[o] = max(rm[rs], rm[ls] + a[rs] - b[rs]);\]
\[lp[o] = max(lp[ls], max(lp[rs] + a[ls] - b[ls], lm[rs] + a[ls] + b[ls]));\]
\[lm[o] = max(lm[ls], lm[rs] - a[ls] + b[ls]);\]
只要有這四個 所有情況全都構造的出來了qwq
然後我們可以直接通過這些計算答案 \(ans\) 了
\[ans[o] = max(max(ans[ls], ans[rs]), max(rp[ls] + lm[rs], rm[ls] + lp[rs]));\]
然後變黑點的時候 我們將那些東西清零 變白點就清成 \(-inf\) 就行了
本文解釋的比較差 看詳細構造推薦 這篇部落格 !!!
程式碼
/**************************************************************
Problem: 1095
User: zjp_shadow
Language: C++
Result: Accepted
Time:4152 ms
Memory:62440 kb
****************************************************************/
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * fh;
}
inline char read_char() {
char ch = getchar();
for (; !isupper(ch); ch = getchar());
return ch;
}
void File() {
#ifdef zjp_shadow
freopen ("1095.in", "r", stdin);
freopen ("1095.out", "w", stdout);
#endif
}
const int N = 1200010, inf = 0x3f3f3f3f;
const int maxn = N;
int lis[N];
#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r
struct Segment_Tree {
int lp[maxn], rp[maxn], lm[maxn], rm[maxn], a[maxn], b[maxn], ans[maxn];
void push_up(int o, int l, int r) {
int ls = o << 1, rs = ls | 1;
a[o] = a[ls] + max(a[rs] - b[ls], 0);
b[o] = b[rs] + max(b[ls] - a[rs], 0);
rp[o] = max(rp[rs], max(rp[ls] - a[rs] + b[rs], rm[ls] + a[rs] + b[rs]));
rm[o] = max(rm[rs], rm[ls] + a[rs] - b[rs]);
lp[o] = max(lp[ls], max(lp[rs] + a[ls] - b[ls], lm[rs] + a[ls] + b[ls]));
lm[o] = max(lm[ls], lm[rs] - a[ls] + b[ls]);
ans[o] = max(max(ans[ls], ans[rs]), max(rp[ls] + lm[rs], rm[ls] + lp[rs]));
}
void Build(int o, int l, int r) {
if (l == r) {
if (lis[l] > 0) lp[o] = rp[o] = lm[o] = rm[o] = ans[o] = 0;
else lp[o] = rp[o] = lm[o] = rm[o] = -inf, ans[o] = -1;
if (lis[l] == -2) b[o] = 1;
if (lis[l] == -1) a[o] = 1;
return ;
}
int mid = (l + r) >> 1; Build(lson); Build(rson);
push_up(o, l, r);
}
void Update(int o, int l, int r, int up) {
if (l == r) {
if (lp[o] > -inf) lp[o] = rp[o] = lm[o] = rm[o] = -inf, ans[o] = -1;
else lp[o] = rp[o] = lm[o] = rm[o] = ans[o] = 0;
return ;
}
int mid = (l + r) >> 1;
if (up <= mid) Update(lson, up); else Update(rson, up);
push_up(o, l, r);
}
} T;
#undef lson
#undef rson
vector<int> G[N];
int n, clk = 0, pos[N];
void Dfs(int u, int fa) {
lis[++ clk] = -2;
lis[pos[u] = ++ clk] = u;
For(i, 0, G[u].size() - 1) { int v = G[u][i]; if (v != fa) Dfs(v, u); }
lis[++ clk] = -1;
}
int main () {
File();
n = read();
For (i, 1, n - 1) {
int u = read(), v = read();
G[u].push_back(v);
G[v].push_back(u);
}
Dfs(1, 0);
T.Build(1, 1, clk);
int m = read();
For (i, 1, m) {
char opt = read_char();
if (opt == 'C')
T.Update(1, 1, clk, pos[read()]);
else
printf ("%d\n", T.ans[1]);
}
return 0;
}