BZOJ1095 [ZJOI2007] Hide 捉迷藏 (括号序列 + 线段树)

题意

给你一颗有 n 个点的树 , 共有 m 次操作 有两种类别qwq

  1. 将树上一个点染黑/白;
  2. 询问树上最远的两个黑点的距离.

(n200000,m500000)

题解

  • 树上距离如果不带权的话我们很容易用一个括号序列来维护qwq

    进来的时候我们添加一个左括号 把这个数字放进来 出去的时候我们添加一个右括号

    其实这个和欧拉序差不多

    比如 这颗树的括号序列就是 (1(2)(3(4)(5(6(7))))(8))

    然后有一个显然的定理

    对于树上任意两个点 , 它们之间的距离等于这两个数字之间未匹配的括号数量

    这个比较显然 我们可以这样考虑 两个点到他们 LCA 一个全为 ) 一个全为 (

    这是因为中间和上面的括号都已经全部匹配完了 然后距离就是它们加起来了

    我们需要维护的就是树上两个黑点之间未匹配的括号数的最大值

    大概都长这个样子 ))))((((

    我们考虑用线段树维护这个东西

    这个看起来比较难以维护 所以我们需要一些辅助的东西才能进行维护

    接下来的定义 都要在去掉 匹配括号的条件 下进行!!!

    定义 o 为线段树上当前的节点 ls 为当前节点在的左儿子 rs 为右儿子

    1. 需要维护当前区间右括号 a 和左括号 b 的数量

      然后我们有两个显然的转移

      a[o]=a[ls]+max(a[rs]b[ls],0);b[o]=b[rs]+max(b[ls]a[rs],0);

    2. 然后我们需要维护另外四个东西 , 就是

      从当前序列中一个黑点到序列两端的未匹配括号和的最大值 和 差的最大值

      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

    3. 然后我们可以直接通过这些计算答案 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; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/8672799.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(770)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示