标签: 平衡树

[IOI2011]Dancing Elephants 题解

[IOI2011]Dancing Elephants 题解

题目地址:CodeChef:Elephant | CodeChef

题目描述

Pattaya 的大象跳舞表演非常有名。整个表演中,N 只大象站成一排跳舞。
经过多年的训练,大象们能够在表演时做很多迷人的舞蹈动作。表演由一系列的动作组成。在每个动作中,只有一只大象可能会移动到一个新的位置上。
大象表演的组织者想要拍摄一本包括全部动作的相册。在每个动作之后,他们要拍摄到所有的大象。
在表演中的任何时刻,多只大象可能站在同一个位置上。在这种情况下,在同一个位置上的大象会从前到后站成一排。
一架相机的拍摄宽度为 L(包括两个端点),即一架相机可以拍摄到位于连续的 L+1 个位置上的大象(有些位置可能没有大象)。因为舞台比较大,所以需要多架相机才能同时拍摄到所有的大象。
在这个题目中,你需要确定每一个动作之后,至少需要多少架相机才能同时拍摄到全部的大象。注意,所需相机的最小数目会随着动作的进行而增加、减少或者保持不变。

输入输出格式

输入格式:
第一行N、M、L。
下面N行表示大象的初始位置。
下面M行一行两个整数,第i只大象到了Pi位置。

输出格式:
每次动作后输出最少相机数。

输入输出样例

输入样例#1:

4 10 5
10
15
17
20
2 16
1 25
3 35
0 38
2 0

输出样例#1:

1
2
2
2
3

输入样例#2:

2 12321 3
2
123
1 76543
0 66321
0 78754

输出样例#2:

2
1
1

说明

1≤N≤150000

题解

如果不改变大象的位置,我们考虑怎么解决。我们从第一只大象开始,每次设置一只摄像机来覆盖L距离内的大象,然后往后找第一只没有被覆盖的大象,再设置相机。最后统计设置了多少相机即可。
现在我们需要改变大象的位置,又不能每一次全部扫一遍,自然需要一个快速统计答案的方法。首先我们考虑一种建图方法,先将所有可能出现大想的位置离散化出来,对于相邻位置连边。每增加一只大象,切断该点与后面的点的边,并且连边至超过L距离的第一个点。将有大象的点的点权设置为1,没有的为0,统计起点到终点的路径点权和即为答案。我们考虑可以用LCT来维护这个图,因为上面不可能有环,每次实际上维护的是一棵树。LCT的功能能够满足我们的需求。

代码

以下代码参考了这个Solution:Solution: 16828126 | CodeChef,感谢原作者kazuma。

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <algorithm>
#include <vector>

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 1000005, INF = 1e9;

struct LCTNode {
    int ch[2], fa, val, sum;
    bool rev;
} lct[MAXN];

inline bool isleft(int p) {
    return lct[lct[p].fa].ch[0] == p;
}

inline bool isroot(int p) {
    register int fa = lct[p].fa;
    return lct[fa].ch[0] != p && lct[fa].ch[1] != p;
}

inline void update(int p) {
    register int ls = lct[p].ch[0], rs = lct[p].ch[1];
    lct[p].sum = lct[p].val + lct[ls].sum + lct[rs].sum;
}

inline void reverse(int p) {
    std::swap(lct[p].ch[0], lct[p].ch[1]);
    lct[p].rev ^= 1;
}

inline void pushdown(int p) {
    register int ls = lct[p].ch[0], rs = lct[p].ch[1];
    if(lct[p].rev) {
        if(ls) reverse(ls);
        if(rs) reverse(rs);
        lct[p].rev ^= 1;
    }
}

int sta[MAXN], stop;

inline void pushto(int p) {
    stop = 0;
    while(!isroot(p)) {
        sta[stop++] = p;
        p = lct[p].fa;
    }
    pushdown(p);
    while(stop) {
        pushdown(sta[--stop]);
    }
}

inline void rotate(int p) {
    register bool t = !isleft(p); register int fa = lct[p].fa, ffa = lct[fa].fa;
    lct[p].fa = ffa; if(!isroot(fa)) lct[ffa].ch[!isleft(fa)] = p;
    lct[fa].ch[t] = lct[p].ch[!t]; lct[lct[fa].ch[t]].fa = fa;
    lct[p].ch[!t] = fa; lct[fa].fa = p;
    update(fa);
}

inline void splay(int p) {
    pushto(p);
    for(register int fa = lct[p].fa; !isroot(p); rotate(p), fa = lct[p].fa) {
        if(!isroot(fa)) rotate(isleft(fa) == isleft(p) ? fa : p);
    }
    update(p);
}

inline void access(int p) {
    for(register int q = 0; p; q = p, p = lct[p].fa) {
        splay(p);
        lct[p].ch[1] = q;
        update(p);
    }
}

inline void makert(int p) {
    access(p);
    splay(p);
    reverse(p);
}

inline int findrt(int p) {
    access(p);
    splay(p);
    while(lct[p].ch[0]) p = lct[p].ch[0];
    return p;
}

inline void split(int u, int v) {
    makert(u);
    access(v);
    splay(v);
}

inline void link(int u, int v) {
    split(u, v);
    lct[u].fa = v;
}

inline void cut(int u, int v) {
    split(u, v);
    if(lct[v].ch[0] != u || lct[lct[v].ch[0]].ch[1]) return;
    lct[u].fa = lct[v].ch[0] = 0;
}

inline int query(int u, int v) {
    split(u, v);
    return lct[v].sum;
}

inline void modify(int u, int w) {
    access(u);
    splay(u);
    lct[u].val = w;
    update(u);
}

int n, l, m, p[MAXN], x[MAXN], y[MAXN], cnt[MAXN];
std::vector<int> vec;

int main() {
    n = readint(); l = readint(); m = readint();
    vec.push_back(-1);
    vec.push_back(2e9);
    for(int i = 1; i <= n; i++) {
        p[i] = readint();
        vec.push_back(p[i]);
        vec.push_back(p[i] + l + 1);
    }
    for(int i = 1; i <= m; i++) {
        x[i] = readint() + 1; y[i] = readint();
        vec.push_back(y[i]);
        vec.push_back(y[i] + l + 1);
    }
    std::sort(vec.begin(), vec.end());
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
    int siz = vec.size();
    for(int i = 2; i <= siz; i++) {
        link(i - 1, i);
    }
    for(int i = 1; i <= n; i++) {
        int u = std::lower_bound(vec.begin(), vec.end(), p[i]) - vec.begin(),
            v = std::lower_bound(vec.begin(), vec.end(), p[i] + l + 1) - vec.begin();
        if(!cnt[u]) {
            cut(u, u + 1);
            link(u, v);
            modify(u, 1);
        }
        cnt[u]++;
    }
    for(int i = 1; i <= m; i++) {
        int ou = std::lower_bound(vec.begin(), vec.end(), p[x[i]]) - vec.begin(),
            ov = std::lower_bound(vec.begin(), vec.end(), p[x[i]] + l + 1) - vec.begin(),
            u = std::lower_bound(vec.begin(), vec.end(), y[i]) - vec.begin(),
            v = std::lower_bound(vec.begin(), vec.end(), y[i] + l + 1) - vec.begin();
        cnt[ou]--;
        if(!cnt[ou]) {
            cut(ou, ov);
            link(ou, ou + 1);
            modify(ou, 0);
        }
        if(!cnt[u]) {
            cut(u, u + 1);
            link(u, v);
            modify(u, 1);
        }
        cnt[u]++;
        p[x[i]] = y[i];
        printf("%d\n", query(1, siz));
    }
    return 0;
}
[HNOI2010]弹飞绵羊 题解

[HNOI2010]弹飞绵羊 题解

题目地址:洛谷:【P3203】[HNOI2010]弹飞绵羊 – 洛谷、BZOJ:Problem 2002. — [Hnoi2010]Bounce 弹飞绵羊

题目描述

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

输入输出格式

输入格式:
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1。
接下来一行有n个正整数,依次为那n个装置的初始弹力系数。
第三行有一个正整数m,
接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。

输出格式:
对于每个i=1的情况,你都要输出一个需要的步数,占一行。

输入输出样例

输入样例#1:

4
1 2 1 1
3
1 1
2 1 1
1 1

输出样例#1:

2
3

说明

对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

题解

我们可以考虑把第i个弹力装置和第i+ki个连边,这样我们会建出来一棵树。如果跳超过了就直接连到n+1号点上。如果不改ki的值,答案即为以n+1为根的有根树中i号点的深度。现在要改ki的值,就得动态维护树上信息,就得用LCT。改的时候,切掉i和i+ki的边,然后再加i和新i+ki的边即可。统计信息就统计子树大小。把i到n+1的链split出来以后,Splay里就只有这条链,因此子树大小就是答案。

代码

// Code by KSkun, 2018/3
#include <cstdio>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF 
        : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 200005;

// link/cut tree

struct Node {
    int ch[2], fa, sum;
    bool rev;
} tr[MAXN];

inline bool isleft(int p) {
    return tr[tr[p].fa].ch[0] == p;
}

inline bool isroot(int p) {
    int fa = tr[p].fa;
    return tr[fa].ch[0] != p && tr[fa].ch[1] != p;
}

inline void update(int p) {
    int ls = tr[p].ch[0], rs = tr[p].ch[1];
    tr[p].sum = tr[ls].sum + tr[rs].sum + 1;
}

inline void reverse(int p) {
    std::swap(tr[p].ch[0], tr[p].ch[1]);
    tr[p].rev ^= 1;
}

inline void pushdown(int p) {
    int ls = tr[p].ch[0], rs = tr[p].ch[1];
    if(tr[p].rev) {
        if(ls) reverse(ls);
        if(rs) reverse(rs);
        tr[p].rev ^= 1;
    }
}

int sta[MAXN], stop;

inline void pushto(int p) {
    stop = 0;
    while(!isroot(p)) {
        sta[stop++] = p;
        p = tr[p].fa;
    } 
    pushdown(p);
    while(stop) {
        pushdown(sta[--stop]);
    }
}

inline void rotate(int p) {
    bool t = !isleft(p); int fa = tr[p].fa, ffa = tr[fa].fa;
    tr[p].fa = ffa; if(!isroot(fa)) tr[ffa].ch[!isleft(fa)] = p;
    tr[fa].ch[t] = tr[p].ch[!t]; tr[tr[fa].ch[t]].fa = fa;
    tr[p].ch[!t] = fa; tr[fa].fa = p;
    update(fa);
}

inline void splay(int p) {
    pushto(p);
    for(int fa = tr[p].fa; !isroot(p); rotate(p), fa = tr[p].fa) {
        if(!isroot(fa)) rotate(isleft(fa) == isleft(p) ? fa : p);
    }
    update(p);
}

inline void access(int p) {
    for(int q = 0; p; q = p, p = tr[p].fa) {
        splay(p);
        tr[p].ch[1] = q;
        update(p);
    }
}

inline void makert(int p) {
    access(p);
    splay(p);
    reverse(p);
}

inline int findrt(int p) {
    access(p);
    splay(p);
    while(tr[p].ch[0]) p = tr[p].ch[0];
    return p;
}

inline void split(int u, int v) {
    makert(u);
    access(v);
    splay(v);
}

inline void link(int u, int v) {
    split(u, v);
    tr[u].fa = v;
}

inline void cut(int u, int v) {
    split(u, v);
    if(tr[v].ch[0] != u || tr[tr[v].ch[0]].ch[1]) return;
    tr[u].fa = tr[v].ch[0] = 0;
}

int n, m, k[MAXN], op, ut, wt;

int main() {
    n = readint();
    tr[n + 1].sum = 1;
    for(int i = 1; i <= n; i++) {
        k[i] = readint();
        tr[i].sum = 1;
    }
    for(int i = 1; i <= n; i++) {
        int x = i, y = i + k[i];
        y = std::min(y, n + 1);
        link(x, y);
    }
    m = readint();
    while(m--) {
        op = readint(); ut = readint() + 1;
        if(op == 1) {
            split(ut, n + 1);
            printf("%d\n", tr[n + 1].sum - 1);
        } else {
            wt = readint();
            cut(ut, std::min(ut + k[ut], n + 1));
            k[ut] = wt;
            link(ut, std::min(ut + k[ut], n + 1));
        }
    }
    return 0;
}
[HNOI2011]括号修复 题解

[HNOI2011]括号修复 题解

题目地址:洛谷:【P3215】[HNOI2011]括号修复 – 洛谷、BZOJ:Problem 2329. — [HNOI2011]括号修复

题目描述

一个合法的括号序列是这样定义的:

  1. 空串是合法的。
  2. 如果字符串 S 是合法的,则(S)也是合法的。
  3. 如果字符串 A 和 B 是合法的,则 AB 也是合法的。

现在给你一个长度为 N 的由‘(‘和‘)’组成的字符串,位置标号从 1 到 N。对这个字符串有下列四种操作:

  1. Replace a b c:将[a,b]之间的所有括号改成 c。例如:假设原来的字符串为:))())())(,那么执行操作 Replace 2 7 ( 后原来的字符串变为:)(((((()(。
  2. Swap a b:将[a,b]之间的字符串翻转。例如:假设原来的字符串为:))())())(,那么执行操作 Swap 3 5 后原来的字符串变为:))))(())(。
  3. Invert a b:将[a,b]之间的‘(’变成‘)’,‘)’变成‘(’。例如:假设原来的字符串为:))())())(,那么执行操作 Invert 4 8 后原来的字符串变为:))((()(((。
  4. Query a b:询问[a,b]之间的字符串至少要改变多少位才能变成合法的括号序列。改变某位是指将该位的‘(’变成‘)’或‘)’变成‘(’。注意执行操作 Query 并不改变当前的括号序列。例如:假设原来的字符串为:))())())(,那么执行操作 Query 3 6 的结果为 2,因为要将位置 5 的‘)’变成‘(’并将位置 6 的‘(’变成‘)’。

输入输出格式

输入格式:
从文件input.txt中读入数据,输入文件的第一行是用空格隔开的两个正整数N和M,分别表示字符串的长度和将执行的操作个数。第二行是长度为N的初始字符串S。接下来的M行是将依次执行的M个操作,其中操作名与操作数之间以及相邻操作数之间均用空格隔开。

输出格式:
输出文件 output.txt 包含 T 行,其中 T 是输入的将执行的 M 个操作中 Query 操作出现的次数。Query 操作的每次出现依次对应输出文件中的一行,该行只有一个非负整数,表示执行对应 Query 操作的结果,即:所指字符串至少要改变多少位才能变成合法的括号序列。输入数据保证问题有解。

输入输出样例

输入样例#1:

4 5
((((
Replace 1 2 )
Query 1 2
Swap 2 3
Invert 3 4
Query 1 4

输出样例#1:

1
2

说明

样例解释:
输入中有2个Query操作,所以输出有2行。执行第一个Query操作时的括号序列为))((,因改变第1位可使[1,2]之间的字符串变成合法的括号序列,故输出的第一行为1。执行第二个Query操作时的括号序列为)((),因要改变第1位和第2位才能使[1,4]之间的字符串变成合法的括号序列,故输出的第二行为2。

数据范围:
30%的数据满足N,M≤3000。100%的数据满足N,M≤100000。

题解

从操作4入手,问题转化

首先我们考虑一个问题,给一个串,怎么知道它最少改变多少位才能合法。我们发现,去除配对成功的括号后,剩余不能配对的串一定长这样:))))(((。假如这里面有a个),b个(,答案应该是\lceil \frac{a}{2} \rceil + \lceil \frac{b}{2} \rceil。对于括号序列,我们可以用1代替(,用-1代替),这样求个和就会自动消除那些已经匹配的括号。而a就是最小前缀和,b就是最大后缀和。
我们可以用splay维护区间和,区间最大前缀和,区间最大后缀和。有了这些信息,最小前缀和可以通过和-最大后缀和求得。

标记和标记的apply

翻转标记和替换标记很常规,可以参考[NOI2005]维护数列 题解 | KSkun’s Blog
至于invert标记,这个会比较麻烦。它会使总和变为相反数,最大前缀和变为最小前缀和,最大后缀和变为最小后缀和。但是我们可以用总和-最小前缀求得最大后缀,以此类推,这个标记就也不是问题了。

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <algorithm>

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    char c = fgc(); 
    while (c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while (c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

inline bool isop(char c) {
    return c == 'R' || c == 'S' || c == 'I' || c == 'Q';
}

inline char readop() {
    char c;
    while(!isop(c = fgc()));
    return c;
}

inline bool isbracket(char c) {
    return c == '(' || c == ')';
}

inline char readbracket() {
    char c;
    while(!isbracket(c = fgc()));
    return c;
}

// variables

const int MAXN = 100005, INF = 1e9;

int n, m, at, bt, ct, a[MAXN];
char op;

// splay

struct Node {
    int val, sum, mxl, mxr, siz;
    bool rev, rep, inv;
    int ch[2], fa;
} tr[MAXN]; 
int tot = 0, sta[MAXN], stop = 0, rt = 0;

inline void update(int p) {
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    tr[p].siz = tr[lch].siz + tr[rch].siz + 1;
    tr[p].sum = tr[lch].sum + tr[rch].sum + tr[p].val;
    tr[p].mxl = std::max(tr[lch].mxl, tr[lch].sum + tr[p].val + tr[rch].mxl);
    tr[p].mxr = std::max(tr[rch].mxr, tr[rch].sum + tr[p].val + tr[lch].mxr);
}

inline void pushdown(int p) {
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    if(tr[p].rep) {
        tr[p].rep = tr[p].rev = tr[p].inv = false;
        if(lch) {
            tr[lch].rep = true;
            tr[lch].rev = tr[lch].inv = false;
            tr[lch].val = tr[p].val;
            tr[lch].sum = tr[p].val * tr[lch].siz;
            tr[lch].mxl = tr[lch].mxr = std::max(0, tr[lch].sum);
        }
        if(rch) {
            tr[rch].rep = true;
            tr[rch].rev = tr[rch].inv = false;
            tr[rch].val = tr[p].val;
            tr[rch].sum = tr[p].val * tr[rch].siz;
            tr[rch].mxl = tr[rch].mxr = std::max(0, tr[rch].sum);
        }
    }
    if(tr[p].rev) {
        tr[p].rev = false;
        if(lch) {
            std::swap(tr[lch].mxl, tr[lch].mxr);
            std::swap(tr[lch].ch[0], tr[lch].ch[1]);
            tr[lch].rev = !tr[lch].rev;
        }
        if(rch) {
            std::swap(tr[rch].mxl, tr[rch].mxr);
            std::swap(tr[rch].ch[0], tr[rch].ch[1]);
            tr[rch].rev = !tr[rch].rev;
        }
    }
    if(tr[p].inv) {
        tr[p].inv = false;
        if(lch) {
            int omxl = tr[lch].mxl;
            tr[lch].inv = !tr[lch].inv;
            tr[lch].mxl = std::max(0, -(tr[lch].sum - tr[lch].mxr));
            tr[lch].mxr = std::max(0, -(tr[lch].sum - omxl));
            tr[lch].sum = -tr[lch].sum;
            tr[lch].val = -tr[lch].val;
        }
        if(rch) {
            int omxl = tr[rch].mxl;
            tr[rch].inv = !tr[rch].inv;
            tr[rch].mxl = std::max(0, -(tr[rch].sum - tr[rch].mxr));
            tr[rch].mxr = std::max(0, -(tr[rch].sum - omxl));
            tr[rch].sum = -tr[rch].sum;
            tr[rch].val = -tr[rch].val;
        }
    }
}

inline int newnode() {
    int p;
    if(stop > 0) {
        p = sta[--stop];
    } else {
        p = ++tot;
    } 
    memset(tr + p, 0, sizeof(Node));
    return p;
}

inline void delnode(int a) {
    sta[stop++] = a;
}

inline bool isleft(int p) {
    return tr[tr[p].fa].ch[0] == p;
}

inline void rotate(int p) { // p is child
    bool type = !isleft(p);
    int fa = tr[p].fa, ffa = tr[fa].fa;
    tr[fa].ch[type] = tr[p].ch[!type];
    tr[p].ch[!type] = fa;
    tr[tr[fa].ch[type]].fa = fa;
    if(ffa) tr[ffa].ch[!isleft(fa)] = p;
    tr[p].fa = tr[fa].fa;
    tr[fa].fa = p;
    update(fa);
    update(p);
}

inline void splay(int p, int tar) {
    for(int fa; (fa = tr[p].fa) != tar; rotate(p)) {
        if(tr[tr[p].fa].fa != tar) {
            rotate(isleft(fa) == isleft(p) ? fa : p);
        }
    }
    if(!tar) rt = p;
}

inline int find(int p, int rk) {
    pushdown(p);
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    if(tr[lch].siz + 1 == rk) return p;
    else if(tr[lch].siz >= rk) return find(lch, rk);
    else return find(rch, rk - tr[lch].siz - 1);
}

inline int build(int fa, int l, int r) {
    if(l > r) return 0;
    int p = newnode();
    if(l == r) {
        tr[p].siz = 1;
        tr[p].val = tr[p].sum = a[l];
        tr[p].mxl = tr[p].mxr = std::max(0, a[l]);
        tr[p].fa = fa;
        return p;
    }
    int mid = (l + r) >> 1;
    tr[p].ch[0] = build(p, l, mid - 1);
    tr[p].ch[1] = build(p, mid + 1, r);
    tr[p].fa = fa;
    tr[p].val = a[mid];
    update(p);
    return p;
}

inline void replace(int x, int len, int val) {
    int a = find(rt, x), b = find(rt, x + len + 1);
    splay(a, 0);
    splay(b, a);
    int p = tr[b].ch[0];
    tr[p].val = val;
    tr[p].rep = true;
    tr[p].sum = val * tr[p].siz;
    tr[p].mxl = tr[p].mxr = std::max(0, tr[p].sum);
    update(b);
    update(a);
}

inline void reverse(int x, int len) {
    int a = find(rt, x), b = find(rt, x + len + 1);
    splay(a, 0);
    splay(b, a);
    int p = tr[b].ch[0];
    if(!tr[p].rep) {
        tr[p].rev = !tr[p].rev;
        std::swap(tr[p].mxl, tr[p].mxr);
        std::swap(tr[p].ch[0], tr[p].ch[1]);
        update(b);
        update(a);
    }
}

inline void invert(int x, int len) {
    int a = find(rt, x), b = find(rt, x + len + 1);
    splay(a, 0);
    splay(b, a);
    int p = tr[b].ch[0];
    int omxl = tr[p].mxl;
    tr[p].inv = !tr[p].inv;
    tr[p].mxl = std::max(0, -(tr[p].sum - tr[p].mxr));
    tr[p].mxr = std::max(0, -(tr[p].sum - omxl));
    tr[p].sum = -tr[p].sum;
    tr[p].val = -tr[p].val;
}

inline int query(int x, int len) {
    int a = find(rt, x), b = find(rt, x + len + 1);
    splay(a, 0);
    splay(b, a);
    int p = tr[b].ch[0];
    return (tr[p].mxr - tr[p].sum + 1) / 2 + (tr[p].mxr + 1) / 2;
}

int main() {
    n = readint();
    m = readint();
    for(int i = 1; i <= n; i++) {
        a[i + 1] = readbracket() == '(' ? 1 : -1;
    }
    rt = build(0, 1, n + 2);
    n += 2;
    while(m--) {
        op = readop();
        at = readint();
        bt = readint();
        if(op == 'R') {
            ct = readbracket() == '(' ? 1 : -1;
            replace(at, bt - at + 1, ct);
        }
        if(op == 'S') {
            reverse(at, bt - at + 1);
        }
        if(op == 'I') {
            invert(at, bt - at + 1);
        }
        if(op == 'Q') {
            printf("%d\n", query(at, bt - at + 1));
        }
    }
    return 0;
}