P10513 括号

P10513 括号

一、题目简析

本题采用 线段树 求解。

节点的定义

struct node
{
	int l, r;
	int lcnt, rcnt;     // lcnt -- (的个数; rcnt -- )的个数 
	int ans, anti;      // ans -- ()的个数; anti -- )(的个数 
	bool tag;           // true -- 需要翻转左右孩子 
} tree[N << 2];

对于 \([l,r]\) 的括号序列,合法的括号对数,有三个贡献:

  • 仅在左半部分 \([l,m]\) 形成的合法括号对数 ()
  • 仅在右半部分 \([m+1,r]\) 形成的合法括号对数 ()
  • 由左右两部分一起提供括号形成的括号对数 (),即 \([l,m]\) 提供 (,为 lcnt - ans\([m+1,r]\) 提供 ),为 rcnt - ans

因此,节点不仅要有 ans (记录 ()),而且要有 lcnt (记录 ()和 rcnt (记录 ))。

除此以外,还需要 anti,记录 )(。翻转一次括号序列 \([l.r]\),翻转后的结果,只需要分别交换 lcntrcntansanti。有了 anti,就能快速得到 ans

对于 \([l,r]\) 的括号序列,anti 也有三个贡献:

  • 仅在左半部分 \([l,m]\) 形成的 )(
  • 仅在右半部分 \([m+1,r]\) 形成的 )(
  • 由左右两部分一起提供括号形成的 )(,即 \([l,m]\) 提供 ),为 rcnt - anti\([m+1,r]\) 提供 (,为 lcnt - anti

合并信息

void add(node &p, node &a, node &b)
{
	p.lcnt = a.lcnt + b.lcnt;
	p.rcnt = a.rcnt + b.rcnt;
	p.ans = a.ans + b.ans + min(a.lcnt - a.ans, b.rcnt - b.ans);
	p.anti = a.anti + b.anti + min(a.rcnt - a.anti, b.lcnt - b.anti);
}

void pushup(int p)
{
	add(tree[p], tree[lc(p)], tree[rc(p)]);
}

翻转操作

void amend(node &p)
{
	swap(p.lcnt, p.rcnt);
	swap(p.ans, p.anti);
}

Code

#include <iostream>

using namespace std;
typedef long long ll;

ll quickin(void)
{
	ll ret = 0;
	bool flag = false;
	char ch = getchar();
	while (ch < '0' || ch > '9')
	{
		if (ch == '-')    flag = true;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9' && ch != EOF)
	{
		ret = ret * 10 + ch - '0';
		ch = getchar();
	}
	if (flag)    ret = -ret;
	return ret;
}

#define lc(p)    p << 1
#define rc(p)    p << 1 | 1
const int N = 5e5 + 5;
int n, m;
char s[N];
struct node
{
	int l, r;
	int lcnt, rcnt;     // lcnt -- (的个数; rcnt -- )的个数 
	int ans, anti;      // ans -- ()的个数; anti -- )(的个数 
	bool tag;           // true -- 需要翻转左右孩子 
} tree[N << 2];

void add(node &p, node &a, node &b)
{
	p.lcnt = a.lcnt + b.lcnt;
	p.rcnt = a.rcnt + b.rcnt;
	p.ans = a.ans + b.ans + min(a.lcnt - a.ans, b.rcnt - b.ans);
	p.anti = a.anti + b.anti + min(a.rcnt - a.anti, b.lcnt - b.anti);
}

void amend(node &p)
{
	swap(p.lcnt, p.rcnt);
	swap(p.ans, p.anti);
}

void pushup(int p)
{
	add(tree[p], tree[lc(p)], tree[rc(p)]);
}

void pushdown(int p)
{
	if (tree[p].tag)
	{
		amend(tree[lc(p)]);
		tree[lc(p)].tag ^= 1;
		
		amend(tree[rc(p)]);
		tree[rc(p)].tag ^= 1;
		
		tree[p].tag = false;
	}
}

void build(int p, int l, int r)
{
	tree[p].l = l, tree[p].r = r;
	if (l == r)
	{
		tree[p].lcnt = s[l] == '(';
		tree[p].rcnt = s[l] == ')';
		return;
	}
	
	int m = l + r >> 1;
	build(lc(p), l, m);
	build(rc(p), m + 1, r);
	
	pushup(p);
}

void update(int p, int x, int y)
{
	if (x <= tree[p].l && tree[p].r <= y)
	{
		amend(tree[p]);
		tree[p].tag ^= 1;
		return;
	}
	
	pushdown(p);
	
	int m = tree[p].l + tree[p].r >> 1;
	if (x <= m)    update(lc(p), x, y);
	if (y > m)    update(rc(p), x, y);
	
	pushup(p);
}

node query(int p, int x, int y)
{
	if (x <= tree[p].l && tree[p].r <= y)
		return tree[p];
		
	pushdown(p);
	
	int m = tree[p].l + tree[p].r >> 1;
	node a = {0}, b = {0}, res = {0};        // 不能简单地将两个区间的ans相加, 
	if (x <= m)    a = query(lc(p), x, y);   // 还有两个区间共同形成的() 
	if (y > m)    b = query(rc(p), x, y);    // 所以必须将两个区间的信息合并 
	
	add(res, a, b);
	return res;
}

int main()
{
	#ifdef LOCAL
	freopen("test.in", "r", stdin);
	#endif
	
	n = quickin();
	scanf("%s", s + 1);
	
	build(1, 1, n);
	
	m = quickin();
	for (int i = 0; i < m; ++i)
	{
		int a, b, c;
		a = quickin(), b = quickin(), c = quickin();
		
		if (a == 1)
			update(1, b, c);
		else if (a == 2)
			printf("%d\n", query(1, b, c).ans);
	}
	
	return 0;
}

posted @ 2024-05-23 10:31  ltign  阅读(9)  评论(0编辑  收藏  举报