首页 > 其他分享 >P8765 [蓝桥杯 2021 国 AB] 翻转括号序列

P8765 [蓝桥杯 2021 国 AB] 翻转括号序列

时间:2024-05-24 21:09:11浏览次数:11  
标签:P8765 AB int text update mmin 蓝桥 add presum

本文参考博客 [蓝桥杯 2021 国 AB] 翻转括号序列(线段树上二分)

一、问题简析

线段树 + 二分

初步分析

( 的值为 1) 的值为 -1,则对于序列 \(a_La_{L+1}a_{L+2}...a_R\),其为合法序列的条件为

\[\begin{cases} \sum_{n=L}^R{a_n}=0 \\ \forall ~k\in [L,R],\sum_{n=L}^k{a_n} \ge 0 \end{cases} \]

用前缀和 presum 来表示,则为

\[\begin{cases} \text{presum[R]}=\text{presum[L - 1]} \\ \forall ~k\in [L,R],\text{presum[k]} \ge \text{presum[L - 1]} \end{cases} \]

因此,我们需要维护区间前缀和的最小值。

建立线段树

节点信息

struct node
{
	int l, r;
	int mmax, mmin;         // [l, r]中前缀和的最大值为mmax,最小值为mmin 
	int tag_rev, tag_add;   // tag_rev -- 是否需要翻转; tag_add -- 待增加的值 
} tree[N << 2];

操作一

操作一要翻转 \([L,R]\) 中的括号,我们可以将该操作分成两部分:

\[reverse(L,R)=reverse(1,L-1)+reverse(1,R) \]

为什么要这样做呢?我们来看翻转序列 \([1,R]\)。翻转后,会对整个数列的前缀和产生影响,进而影响维护的 mminmmax 产生影响。

  • 对于 \([1,R]\),相当于 \(\text{presum[1,2, ...,R]}\) 取相反数。因此 mminmmax 交换,并取相反数即可。
  • 对于 \([R+1,n]\),因为只取反了 \([1,R]\),所以在 \(-\text{presum[R]}\) 的基础上加上原来的数,相当于在原来的前缀和上减去两倍的 \(\text{presum[R]}\)。因此, mminmmax 也要减去两倍的 \(\text{presum[R]}\)。

所以,要翻转序列 \([1,R]\),要先后进行两种更新:

  • 更新一,区间 \([1,R]\) 的前缀和取反,即 mminmmax 交换,并取相反数。
  • 更新二,区间 \([R+1,n]\) 的前缀和减去两倍的 \(\text{presum[R]}\),即 mminmmax 减去两倍的 \(\text{presum[R]}\)。

因此,我们需要两个懒惰标记。值得注意的是,tag_rev 会对 tag_add 产生影响——令 tag_add 取相反数,反过来则不会有影响。在进行 pushdown 时,要先更新 tag_rev,再是 tag_add。(作者尚未明白原因)

操作二

操作二要用二分来实现。我们先来看二分的前提条件——单调性。区间 \([L,R]\) 前缀和的最小值 mmin 随 \(R\) 单调不增。因此,我们可以利用二分找到最大的 \(R\),满足区间 \([L,R]\) 前缀和的最小值 mmin 大于等于 \(\text{presum[L - 1]}\)。再判断此时的 \(R\) 是否满足 \(\text{presum[R]}=\text{presum[L - 1]}\)。
因此,query 的作用是查找区间 \([L,R]\) 前缀和的最小值 mmin

需要注意,二分的条件是:

  • 若 \([L,M]\) 的 mmin 小于 \(\text{presum[L - 1]}\),令 \(R=M-1\)。
  • 若 \([L,M]\) 的 mmin 大于等于 \(\text{presum[L - 1]}\) 且 \([M,R]\) 的 mmin 大于 \(\text{presum[L - 1]}\),也要令 \(R=M-1\)。
  • 否则,令 \(L=M+1\)。

二、Code

P8765 [蓝桥杯 2021 国 AB] 翻转括号序列

#include <bits/stdc++.h>

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 = 1e6 + 5;
int n, m;
int presum[N];
char s[N];
struct node
{
	int l, r;
	int mmax, mmin;         // [l, r]中前缀和的最大值为mmax,最小值为mmin 
	int tag_rev, tag_add;   // tag_rev -- 是否需要翻转; tag_add -- 待增加的值 
} tree[N << 2];

void amend_rev(node &p)
{
	int tmp1 = p.mmax, tmp2 = p.mmin;
	p.mmax = -tmp2;
	p.mmin = -tmp1;
	p.tag_rev ^= 1;
	p.tag_add *= -1;
}

void amend_add(node &p, int val)
{
	p.mmax += val;
	p.mmin += val;
	p.tag_add += val;
}

void pushup(int p)
{
	tree[p].mmax = max(tree[lc(p)].mmax, tree[rc(p)].mmax);
	tree[p].mmin = min(tree[lc(p)].mmin, tree[rc(p)].mmin);
}

void pushdown(int p)
{
	if (tree[p].tag_rev)
	{
		amend_rev(tree[lc(p)]);
		amend_rev(tree[rc(p)]);
		
		tree[p].tag_rev = 0;
	}
	if (tree[p].tag_add)
	{
		amend_add(tree[lc(p)], tree[p].tag_add);
		amend_add(tree[rc(p)], tree[p].tag_add);
		
		tree[p].tag_add = 0;
	}
}

void build(int p, int l, int r)
{
	tree[p] = {l, r, presum[l], presum[l], 0, 0};
	if (l == r)    return;
	
	int m = (l + r) >> 1;
	build(lc(p), l, m);
	build(rc(p), m + 1, r);
	
	pushup(p);
}

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

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

int query(int p, int x, int y)
{	
	if (x == 0 && y == 0)    return 0;
	if (x <= tree[p].l && tree[p].r <= y)
		return tree[p].mmin;
	
	pushdown(p);
	
	int m = (tree[p].l + tree[p].r) >> 1;
	int ans = 1e8;
	if (x <= m)    ans = min(ans, query(lc(p), x, y));
	if (y > m)    ans = min(ans, query(rc(p), x, y));
	
	return ans;
}

void update(int x, int y)
{
	int val = query(1, x - 1, x - 1);
	update_rev(1, 1, x - 1);
	update_add(1, x, n, -2 * val);
	
	val = query(1, y, y);
	update_rev(1, x, y);
	update_add(1, y + 1, n, -2 * val);
}

int main()
{
	#ifdef LOCAL
	freopen("test.in", "r", stdin);
	#endif
	
	n = quickin(), m = quickin();
	scanf("%s", s + 1);
	for (int i = 1; i <= n; ++i)
	{
		presum[i] = s[i] == '(' ? 1 : -1;
		presum[i] += presum[i - 1];
	}
	
	build(1, 1, n);
	
	for (int i = 0; i < m; ++i)
	{
		int a, b, c;
		a = quickin();
		
		if (a == 1)
		{
			b = quickin(), c = quickin();
			update(b, c);
		}
		else if (a == 2)
		{
			b = quickin();
			int key = query(1, b - 1, b - 1);
			
			int l = b, r = n;
			
			while (l <= r)
			{
				int m = (l + r) >> 1;
				int mmin = query(1, l, m);
				if (mmin < key || mmin >= key && query(1, m, n) > key)
					r = m - 1;
				else
					l = m + 1;
			}
			
			if (r >= b && query(1, r, r) == key)    printf("%d\n", r);
			else    printf("0\n");
		}
	}
	
	return 0;
}

标签:P8765,AB,int,text,update,mmin,蓝桥,add,presum
From: https://www.cnblogs.com/hoyd/p/18211661

相关文章

  • 蓝桥杯-数三角(ac代码时间复杂度分析)
    问题描述小明在二维坐标系中放置了(n)个点,他想在其中选出一个包含三个点的子集,这三个点能组成三角形。然而这样的方案太多了,他决定只选择那些可以组成等腰三角形的方案。请帮他计算出一共有多少种选法可以组成等腰三角形?输入格式输入共(n+1)行。第一行为一个正整数(......
  • 【Rabbitmq使用】docker compose 命令重启rabbitmq后数据丢失问题
    目录0.导火索1.问题和背景2.原因与解决2.1原因2.2解决0.导火索在服务器上使用一个rabbitmq服务,但是需要多个项目使用这一个mq服务,于是就建立了rabbitmq的虚拟主机virtualhost来作为各个服务的迷你版mq来使用;再使用dockercompose命令重启后,某个项目报错如下:......
  • 不闭合三维TSP:蜣螂优化算法DBO求解不闭合三维TSP(起点固定,终点不定,可以更改数据集),MATLA
    一、旅行商问题旅行商问题(Travelingsalesmanproblem,TSP)是一个经典的组合优化问题,它可以描述为一个商品推销员去若干城市推销商品,要求遍历所有城市后回到出发地,目的是选择一个最短的路线。当城市数目较少时,可以使用穷举法求解。而随着城市数增多,求解空间比较复杂,无法使......
  • 50道题目!Python、SQL数据库、AB测试、业务分析、机器学习都在这里了!
    介绍每日一题系列已经更新了50道题目啦!题目难度为初级到中级,涵盖了Python、SQL数据库、AB测试、业务分析、机器学习五大主题,适合初学者和有一定基础的朋友。原文链接:50道题目!Python、SQL数据库、AB测试、业务分析、机器学习都在这里了!欢迎点击取阅!......
  • 免费,Python蓝桥杯等级考试真题--第10级(含答案解析和代码)
    Python蓝桥杯等级考试真题–第10级一、选择题1、已知s='Hello!’,下列说法正确的是?()A.s[1]对应的字符是’H’B.s[2]对应的字符是’l’C.s[-1]对应的字符是’o’D.s[3]对应的字符是’o’答案:B解析:s[1]对应字符是‘e’;s[2]对应字符是‘l’;s[-1]对应字符是‘e!;s[3]......
  • AoPS - Chapter 19 Probability
    本章介绍了一些概率的基本概念与条件概率。独立与互斥Twoeventsarecalleduncorrelated(orindependent)(独立)iftheyhavenobearingoneachother.\[P(A\capB)=P(A)\timesP(B)\]Twoeventsarecalledmutuallyexclusive(互斥)ifbotheventscannotsimultaneou......
  • 升级openssh前安装zlib报异常configure aborting
    事情是这样的,因为系统漏洞问题,需要升级openssh,从OpenSSH_9.3p1升级到OpenSSH_9.3p2系统版本:CentOS7升级OpenSSH_9.3p2之前需要先升级zlib从官网下载wgethttps://www.zlib.net/zlib-1.3.1.tar.gz解压tar-zxvfzlib-1.3.1.tar.gzcdzlib-1.3.1./configure--prefix=/u......
  • MATLAB基础知识,帮你快速入门【文末送2024最新MATLAB学习教程资料视频+源码】
    1.MATLAB的基本知识1-1基本运算与函数 在MATLAB下进行基本数学运算,只需将运算式直接打入提示号(>>)之後,并按入Enter键即可。例如: >>(5*2+1.3-0.8)*10/25 ans=4.2000 MATLAB会将运算结果直接存入一变数ans,代表MATLAB运算後的答案(Answer)并显示其数值於萤幕上。小......
  • zabbix - [03] 安装部署
    题记部分 一、准备工作1.1、服务器角色规划主机名IP地址角色备注ctos79-01192.168.2.121zabbix-server开启监控功能ctos79-02192.168.2.122zabbix-agent ctos79-03192.168.2.133zabbix-agent 1.2、关闭防火墙和SELinuxsetenforce0sed-i......
  • Zabbix02-zabbix安装
    安装步骤安装LNMP平台源码安装zabbix安装监控端主机,修改基本配置初始化zabbix监控web页面修改php配置文件,满足zabbix需求安装被监控端主机,修改基本配置zabbix升级https://blog.csdn.net/weixin_44082324/article/details/108732080搭建zabbix监控服务器端1.安装lnmp......