题意
给你一颗有 \(n\) 个点的树 , 共有 \(m\) 次操作 有两种类别qwq
- 将树上一个点染黑/白;
- 询问树上最远的两个黑点的距离.
\((n \le 200000, m ≤500000)\)
题解
树上距离如果不带权的话我们很容易用一个括号序列来维护qwq
进来的时候我们添加一个左括号 把这个数字放进来 出去的时候我们添加一个右括号
其实这个和欧拉序差不多
比如 这颗树的括号序列就是 \((1(2)(3(4)(5(6(7))))(8))\)
然后有一个显然的定理
我们需要维护的就是树上两个黑点之间未匹配的括号数的最大值
大概都长这个样子 \())))((((\)
我们考虑用线段树维护这个东西
这个看起来比较难以维护 所以我们需要一些辅助的东西才能进行维护
接下来的定义 都要在去掉 匹配括号的条件 下进行!!!
定义 \(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;
}