zoukankan      html  css  js  c++  java
  • BZOJ1095 [ZJOI2007] Hide 捉迷藏 (括号序列 + 线段树)

    题意

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

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

    ((n le 200000, m ≤500000))

    题解

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

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

      其实这个和欧拉序差不多

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

      然后有一个显然的定理

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

      这个比较显然 我们可以这样考虑 两个点到他们 (mathrm{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
    ", T.ans[1]);
        }
        return 0;
    }
    
  • 相关阅读:
    (转)ReentrantLock与Synchronized同步区别
    (转)Java NIO框架
    (转)Apache Mina网络框架
    (转)java中Executor、ExecutorService、ThreadPoolExecutor介绍
    路由选择协议
    SYN攻击
    网络基础
    [翻译]在Django项目中添加谷歌统计(Google Analytics)
    初识Python-web框架的这两天
    June本地环境搭建
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/8672799.html
Copyright © 2011-2022 走看看