bzoj2209括号序列splay

2209: [Jsoi2011]括号序列

Time Limit: 20 Sec   Memory Limit: 259 MB
Submit: 1325   Solved: 658
[ Submit][ Status][ Discuss]

Description

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

Sample Output

2
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
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值