6716. 【2020.06.11省选模拟】T2 反讽

题目


正解

左括号记为 + 1 +1 +1,右括号记为 − 1 -1 1
首先答案显然等于 左 括 号 个 数 − 右 括 号 个 数 + 2 ∣ m i n ( 0 , 前 缀 m i n ) ∣ 左括号个数-右括号个数+2|min(0,前缀min)| +2min(0,min)
隔壁大佬的方法有一部分听得不是很懂,所以这里就只将gmh77的方法。

考虑这样的模型:有个二元组 ( a , b ) (a,b) (a,b),表示当前的值如果大于等于 a a a,那么就会得到 b b b
有个经典问题:如果有若干个 ( a , b ) (a,b) (a,b),用什么顺序选取,才能使在满足值一直大于等于 0 0 0的情况下,一开始所需要的值最小。
打怪兽问题:HDU6326
这个东西可以贪心:定义 ( a , b ) (a,b) (a,b)的优劣关系:

  1. b b b大于等于 0 0 0的比小于 0 0 0的优。
  2. b b b大于等于 0 0 0时, a a a越小越优。
  3. b b b小于 0 0 0时, a + b a+b a+b越大越优。

前面两条都比较显然,但第三条看起来不太好懂。
一种解释是隔壁所云“反过来”:将操作顺序以及符号反过来,原来 ( a , b ) (a,b) (a,b)可以看做先减 a a a再加 a + b a+b a+b,反过来就变成减 a + b a+b a+b a a a,记作 ( a + b , − b ) ′ (a+b,-b)' (a+b,b)。这时候 − b -b b是正的,于是按照 a + b a+b a+b从小到大倒着往前做,就相当于按 a + b a+b a+b从大到小顺着往后做。
或者有一种更加舒服的化学解释方法:
b b b看做热量,当 b < 0 b<0 b<0时,可以看做正在发生吸热反应。
a a a表示发生反应的最低能量。于是 a + b a+b a+b可以理解成活化能。
发生反应之后,活化能化为内能。为了支持后面的反应,转化为内能的活化能尽量要为后面的反应提供能量,使其达到反应发生的最低能量。
为了让活化能不要浪费,按照 a + b a+b a+b从大到小的顺序反应。

于是题目中的每条序列都可以视作若干个这样的二元组。
可以发现,当一个劣的二元组在优的二元组前的时候,操作完劣的二元组之后肯定会立即执行优的二元组。于是把这些二元组合并起来,最终就会形成一个从优到劣的二元组。
两个序列之间按照开头的优劣顺序贪心即可。
题解讲得可能更清楚。
6716. 【2020.06.11省选模拟】T2 反讽_第1张图片


代码

using namespace std;
#include 
#include 
#include 
#define N 1000010
int n,m;
char s[N],t[N];
struct Info{
	int a,b;
} da[N],db[N];
bool cmp(Info x,Info y){
	if (x.b>=0 && y.b>=0)
		return x.a<y.a;
	if (x.b<0 && y.b<0)
		return x.a+x.b>y.a+y.b;
	return x.b>y.b;
}
Info operator+(Info x,Info y){
	return {-min(x.b-y.a,min(-x.a,-y.a)),x.b+y.b};
}
void calc(Info &x,Info y){
	if (y.a>x.b){
		x.a+=y.a-x.b;
		x.b=y.a;
	}
	x.b+=y.b;
}
int ak,bk;
void init(char s[],int n,Info d[],int &k){
	for (int i=1;i<=n;++i)
		s[i]=(s[i]=='('?1:-1);
	k=0;
	for (int i=1;i<=n;++i){
		Info nw={max(-int(s[i]),0),s[i]};
		while (k && cmp(nw,d[k])){
			nw=d[k]+nw;
			--k;
		}
		d[++k]=nw;
	}
}
int main(){
	freopen("irony.in","r",stdin);
	freopen("irony.out","w",stdout);
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%d%d%s%s",&n,&m,s+1,t+1);
		init(s,n,da,ak);
		init(t,m,db,bk);
		int cnt=0;
		for (int i=1;i<=n;++i)
			cnt+=s[i];
		for (int i=1;i<=m;++i)
			cnt+=t[i];
		int i=1,j=1;
		Info ans={0,0};
		while (i<=ak && j<=bk)
			if (cmp(da[i],db[j]))
				calc(ans,da[i++]);
			else
				calc(ans,db[j++]);
		for (;i<=ak;++i)
			calc(ans,da[i]);
		for (;j<=bk;++j)
			calc(ans,db[j]);
		printf("%d\n",cnt+2*ans.a);
	}
	return 0;
}

你可能感兴趣的:(贪心)