[BZOJ2209][[Jsoi2011]括号序列][splay]

50 篇文章 0 订阅
2 篇文章 0 订阅

[BZOJ2209][[Jsoi2011]括号序列][splay]

题目大意:

给定一个长度为N的括号序列,有三种操作:

  • 询问让 A[x],a[x+1],...,a[y] 这个括号序列合法至少需要修改几个括号。

  • A[x],a[x+1],...,a[y] 翻转

  • A[x],a[x+1],...,a[y] 反转(左右括号互换)

思路:

去除已经匹配的括号后,剩下的括号一定是左边剩下 x 个左括号,右边剩下y个右括号且 x y奇偶性相同。比如下面这个括号序列。

))()()())()(

删除已经匹配好的就变成:

)))(,x=3,y=1

这时候的答案是:

{x/2+y/2x/2+1+y/2+1x,y%2=0x,y%2=1

也就是 (x+1)/2+(y+1)/2

对于序列 (l,r) x 是左端点为l,右端点 <=r <script type="math/tex" id="MathJax-Element-660"><=r</script>时,序列中右括号比左括号多的个数的最大值 (>=0) 。当将” (=1 ”,” )=1 ”时,实际上求的就是最小左字段和,而 y <script type="math/tex" id="MathJax-Element-664">y</script>求的则是最大右字段和。注意到题目中有反转的操作,因此最大左字段和和最小右字段和也应当保存下来。

序列可以用splay+lazy标志维护。

代码:
#include <bits/stdc++.h>
const int Maxn = 110005;
using namespace std;

namespace IO {
    inline char get(void) {
        static char buf[1000000], *p1 = buf, *p2 = buf;
        if (p1 == p2) {
            p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
            if (p1 == p2) return EOF;
        }
        return *p1++;
    }
    inline void read(int &x) {
        x = 0; static char c; bool f = 0;
        for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') f = 1;
        for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (f) x = -x;
    }
    inline void read(char &x) {
        x = get();
        while (x != '(' && x != ')') x = get();
    }
    inline void write(int x) {
        if (!x) return (void)puts("0");
        if (x < 0) putchar('-'), x = -x;
        static short s[12], t;
        while (x) s[++t] = x % 10, x /= 10;
        while (t) putchar('0' + s[t--]);
        putchar('\n');
    }
};

int n, m;
int c[Maxn][2], fa[Maxn], sum[Maxn], sz[Maxn], a[Maxn], rt;
bool rev[Maxn], opp[Maxn];

struct Val {
    int l0, l1, r0, r1;
} val[Maxn];

inline int Min(const int &a, const int &b) {
    return a < b ? a : b;
}
inline int Max(const int &a, const int &b) {
    return a > b ? a : b;
}
inline void pushUp(int x) {
    int l = c[x][0], r = c[x][1];
    sum[x] = sum[l] + sum[r] + a[x];
    sz[x] = sz[l] + sz[r] + 1;
    val[x].l0 = Min(val[l].l0, sum[l] + a[x] + val[r].l0);
    val[x].l1 = Max(val[l].l1, sum[l] + a[x] + val[r].l1);
    val[x].r0 = Min(val[r].r0, sum[r] + a[x] + val[l].r0);
    val[x].r1 = Max(val[r].r1, sum[r] + a[x] + val[l].r1);
}
inline void rever(int x) {
    rev[x] ^= 1;
    swap(val[x].l0, val[x].r0);
    swap(val[x].l1, val[x].r1);
}
inline void opposite(int x) {
    opp[x] ^= 1; sum[x] = -sum[x]; a[x] = -a[x];
    swap(val[x].l0, val[x].l1);
    swap(val[x].r0, val[x].r1);
    val[x].l0 = -val[x].l0, val[x].l1 = -val[x].l1;
    val[x].r0 = -val[x].r0, val[x].r1 = -val[x].r1;
}
inline void pushDown(int x) {
    if (rev[x]) {
        rever(c[x][0]), rever(c[x][1]);
        swap(c[x][0], c[x][1]);
        rev[x] = 0;
    }
    if (opp[x]) {
        opposite(c[x][0]), opposite(c[x][1]);
        opp[x] = 0;
    }
}
inline void rotate(int x, int &k) {
    int y = fa[x], z = fa[y], r = (x == c[y][0]);
    y == k ? k = x : c[z][!(c[z][0] == y)] = x;
    fa[x] = z; fa[y] = x; fa[c[x][r]] = y;
    c[y][r ^ 1] = c[x][r]; c[x][r] = y;
    pushUp(y), pushUp(x);
}
inline void splay(int x, int &k) {
    while (x != k) {
        int y = fa[x], z = fa[y];
        if (y != k) {
            if ((c[y][0] == x) ^ (c[z][0] == y)) rotate(x, k);
            else rotate(y, k);
        }
        rotate(x, k);
    }
}
void build(int &k, int l, int r, int last) {
    if (l > r) return (void)(k = 0);
    k = (l + r) >> 1; fa[k] = last;
    if (l == r) {
        sum[k] = a[l]; sz[k] = 1;
        if (a[l] < 0) val[k].l0 = val[k].r0 = -1;
        if (a[l] > 0) val[k].l1 = val[k].r1 = 1;
        return;
    }
    build(c[k][0], l, k - 1, k), build(c[k][1], k + 1, r, k);
    pushUp(k);
}
inline int find(int x, int tar) {
    pushDown(x); int l = c[x][0], r = c[x][1];
    if (tar == sz[l] + 1) return x;
    else if (tar <= sz[l]) return find(l, tar);
    else return find(r, tar - sz[l] - 1);
}
int main(void) {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    IO::read(n), IO::read(m);
    char op; int t, l, r;
    for (int i = 2; i <= n + 1; i++) IO::read(op), a[i] = (op == '(' ? 1 : -1);
    build(rt, 1, n + 2, 0);
    while (m--) {
        IO::read(t), IO::read(l), IO::read(r);

        l = find(rt, l); r = find(rt, r + 2);
        splay(l, rt); splay(r, c[l][1]); 
        int k = c[r][0];
        if (!t) IO::write((val[k].r1 + 1) / 2 - (val[k].l0 - 1) / 2);
        else if (t == 1) opposite(k); else rever(k);
    }
    return 0;
}

完。

By g1n0st

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值