首页 > 其他分享 >P10513 括号

P10513 括号

时间:2024-05-23 10:30:07浏览次数:24  
标签:lcnt rcnt int P10513 括号 anti ans

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;
}

标签:lcnt,rcnt,int,P10513,括号,anti,ans
From: https://www.cnblogs.com/hoyd/p/18207821

相关文章

  • 代码随想录算法训练营第十一天 | 20.有效的括号 1047.删除字符串中的所有相邻 重复项
    20.有效的括号题目链接文章讲解视频讲解思路:遍历字符串,如果栈不为空,则进行匹配   如果匹配则出栈,否则入栈   如果栈为空,直接入栈   遍历结束后栈为空则说明全部匹配,否则没有全部匹配classSolution{public:boolisValid(strings){stack<cha......
  • 代码随想录算法训练营第第11天 | 20. 有效的括号 、1047. 删除字符串中的所有相邻重
    今天的题主要是关于栈的,比较简单,一次性过20.有效的括号讲完了栈实现队列,队列实现栈,接下来就是栈的经典应用了。大家先自己思考一下有哪些不匹配的场景,在看视频我讲的都有哪些场景,落实到代码其实就容易很多了。题目链接/文章讲解/视频讲解:https://programmercarl.com/0020.......
  • 区分import 什么时候使用 花括号{ }
    import与之对应的是export要理清export与 export default1、export与exportdefault均可用于导出常量、函数、文件、模块等2、在一个文件或模块中,export可以导出多个,对应的import导入加//导出exportfunctionfn1(){}exportfunctionfn2(){}exportfunctionfn3(......
  • 递归+回溯解决有效括号问题
    题目描述:数字n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合例如:当n=1时,有效的括号组合['()']当n=2时,有效的括号组合['(())','()()']当n=3时,有效的括号组合有['((()))','(()())','(())()'.'()(())','()()()&......
  • 使用js有效括号匹配封装函数
    点击查看代码functionisValidParentheses(str){//定义一个栈,用于存储待匹配的左括号letstack=[];//定义一个对象,用于快速判断括号是否成对constpairs={')':'(','}':'{',']':'['};//遍历输入字符串for(let......
  • SQL脚本中存在很多括号,无法直观进行匹配。
    解决方案1:SSMS中找到前括号按下空格或tab,会自动匹配到对应的后括号,如下图。解决方案2:使用在线格式化工具进行格式化,该工具格式化功能更强大且会自动去除多余无意义的括号组。https://tool.oschina.net/codeformat/sql在线代码格式化(oschina.net) ......
  • [JS] idea中javascript显示无背景色,不能点击大括号收起代码
    idea idea安装组件File->Settings->pluginsmarketplace搜索安装javascriptandtypescript插件(如果marketplace搜素搜索不到,搜索下installed里是否已经安装过了;如果已经安装过了且勾选框是选中的,去勾选插件,保存。然后重新再勾选上,保存) 效果如下: ......
  • 22. 括号生成-c++
    数字n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。示例1:输入:n=3输出:["((()))","(()())","(())()","()(())","()()()"]示例2:输入:n=1输出:["()"]classSolution{public:vector<string>generat......
  • 代码随想录算法训练营第11天 | 栈与队列 20.有效的括号 1047.删除字符串中的所有相邻
    leetcode20.有效的括号题目20.有效的括号给定一个只包括'(',')','{','}','[',']'的字符串s,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。解题思路实现代码leetcod......
  • 括号转树的模板
    电子书板子:希冀平台:#include<iostream>#include<vector>#include<algorithm>#include<math.h>#include<sstream>#include<string>#include<string.h>#include<iomanip>#include<stdlib.h>#include<map&g......