2209: [Jsoi2011]括号序列
Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 1325 Solved: 658
[ Submit][ Status][ Discuss]
Description
![](http://www.lydsy.com/JudgeOnline/images/2209.jpg)
Input
输入数据的第一行包含两个整数N和Q,分别表示括号序列的长度,以及操作的个数。 第二行包含一个长度为N的括号序列。 接下来Q行,每行三个整数t、x和y,分别表示操作的类型、操作的开始位置和操作的结 束位置,输入数据保证x不小于y。其中t=0表示询问操作、t=1表示反转操作、t=2表示翻转操 作。
Output
对于每一个询问操作,输出一行,表示将括号序列的该子序列修改为配对,所需的最少改动 个数。
Sample Input
6 3
)(())(
0 1 6
0 1 4
0 3 4
)(())(
0 1 6
0 1 4
0 3 4
Sample Output
2
2
0
2
0
HINT
100%的数据满足N,Q不超过10^5
。
Source
稍微有点难
首先显然可以把不同的括号消掉,最后剩下))..((这样
考虑答案,如果有x个)和y个(那么显然x+y必须是偶数否则不合法。若x,y均为奇数那么只要把x-1和y-1个两两配对然后x,y分别再配对一个就可以。答案是(x-1)/2 + (y-1)/2 + 2 = (x + 1) /2 + (y + 1) / 2
若x,y均为偶数直接配对就可以了。答案是x/2 + y / 2
综合起来,答案就是[(x + 1)/2] + [(y + 1)/2],[]是下取整。
我们考虑一对被消掉的配对()也就是(和)被互相抵消,对于y来说,显然如果取到了任意一个不被抵消的)就是不合法的。因此我们规定(为1,)为-1,这样x,y可以分别用左起最小连续子段和和右起最大连续字段和来代替。因为这样满足了抵消为0,不被抵消不够合法(不够优)的原则。这是本题难点。
之后直接上平衡树的套路,不过需要注意的是字段和在没有的情况下是会取到一个不合法的情况,因此计入答案时可以选择特判。不过也可以手动取负来规避,及答案为[(-x+1)/2]+[(y+1)/2]
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 101001
#define inf 0x7fffffff
#define ls ch[p][0]
#define rs ch[p][1]
using namespace std;
int read()
{
char ch = getchar(), pre = '0'; int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, m, root, sz, ch[maxn][2], size[maxn], fa[maxn], a[maxn];
int lmn[maxn], rmn[maxn], lmx[maxn], rmx[maxn], val[maxn], sum[maxn];
bool rev[maxn], ops[maxn];
int wh(int p) {return ch[fa[p]][1] == p;}
void rever(int p) {
swap(ls, rs); swap(lmn[p], rmn[p]); swap(lmx[p], rmx[p]);
rev[p] ^= 1;
}
void opsite(int p) {
swap(lmn[p], lmx[p]); swap(rmn[p], rmx[p]);
lmn[p] = -lmn[p]; lmx[p] = -lmx[p]; rmn[p] = -rmn[p]; rmx[p] = -rmx[p];
sum[p] = -sum[p]; val[p] = -val[p];
ops[p] ^= 1;
}
void pushdown(int p) {
if(rev[p]) {
if(ls) rever(ls);
if(rs) rever(rs);
rev[p] = 0;
}
if(ops[p]) {
if(ls) opsite(ls);
if(rs) opsite(rs);
ops[p] = 0;
}
}
void update(int p) {
size[p] = size[ls] + size[rs] + 1;
sum[p] = sum[ls] + sum[rs] + val[p];
lmx[p] = max(lmx[ls], sum[ls] + val[p] + max(lmx[rs], 0));
lmn[p] = min(lmn[ls], sum[ls] + val[p] + min(lmn[rs], 0));
rmx[p] = max(rmx[rs], sum[rs] + val[p] + max(rmx[ls], 0));
rmn[p] = min(rmn[rs], sum[rs] + val[p] + min(rmn[ls], 0));
}
void Rotate(int p) {
int f = fa[p], g = fa[fa[p]], c = wh(p);
if(g) ch[g][(wh(f))] = p; fa[p] = g;
ch[f][c] = ch[p][c ^ 1]; if(ch[f][c]) fa[ch[f][c]] = f;
ch[p][c ^ 1] = f; fa[f] = p;
update(f);
}
void Splay(int p, int tar) {
for(; fa[p] != tar; Rotate(p))
if(fa[fa[p]] != tar) Rotate(wh(p) == wh(fa[p]) ? fa[p] : p);
update(p);
if(tar == 0) root = p;
}
void Build(int &p, int L, int R, int f) {
fa[p = ++sz] = f; int mid = L + R >> 1; val[p] = a[mid];
if(L < mid) Build(ls, L, mid - 1, p);
if(R > mid) Build(rs, mid + 1, R, p);
update(p);
}
int Kth(int k) {
int p = root, lsize = 0;
while(p) {
pushdown(p);
int cur = lsize + size[ls];
if(k == cur + 1) return p;
if(k <= cur) p = ls;
else {
lsize = cur + 1;
p = rs;
}
}
}
void Rev(int l, int r) {
int f = Kth(l); Splay(f, 0);
int p = Kth(r + 2); Splay(p, f);
rever(ls); update(p); update(f);
}
void Ops(int l, int r) {
int f = Kth(l); Splay(f, 0);
int p = Kth(r + 2); Splay(p, f);
opsite(ls); update(p); update(f);
}
void init() {
n = read(); m = read(); lmn[0] = rmn[0] = inf; lmx[0] = rmx[0] = -inf;
for(int i = 2;i <= n + 1; ++i) {
char ch = getchar();
while(ch != '(' && ch != ')') ch = getchar();
if(ch == '(') a[i] = 1;
else a[i] = -1;
}
a[1] = a[n + 2] = 0;
Build(root, 1, n + 2, 0);
}
int query(int l, int r) {
int f = Kth(l); Splay(f, 0);
int p = Kth(r + 2); Splay(p, f);
return ((-lmn[ls] + 1) >> 1) + ((rmx[ls] + 1) >> 1);
}
void solve() {
for(int i = 1; i <= m; ++i) {
int opt = read(), l = read(), r = read();
if(opt == 0) printf("%d\n", query(l, r));
if(opt == 1) Ops(l, r);
if(opt == 2) Rev(l, r);
}
}
int main()
{
init();
solve();
return 0;
}
/*
6 8
)(())(
1 1 6
2 1 4
0 3 4
0 1 6
2 3 4
1 1 5
0 4 5
0 3 6
*/