ARC120D Bracket Score 2 (模拟)

题面

给一个长度为 2 N 2N 2N 的序列 A A A,定义一个长度为 2 N 2N 2N 的合法括号序列的 得分(score) 为:

  • 对于每对配对的括号 i , j i,j i,j ∣ A i − A j ∣ |A_i-A_j| AiAj 的和。

输出得分最高的任意一个合法括号序列。

1 ≤ N ≤ 2 ⋅ 1 0 5 1\leq N\leq 2\cdot10^5 1N2105.

题解

我们先想一个相关的问题:

在数轴上从左到右存在 2 N 2N 2N 个点(可重合),点之间两两配对并连成线段,问最大的线段总长度。

这是一个经典问题了

考虑第一个点到第二个点之间的部分,最多会被线段覆盖一层,因为左边只有一个点;
第二个点到第三个点之间的部分,最多会被覆盖两层,因为左边只有两个点;
……
依此类推,我们能不能找到一种配对方法,使得每两个点之间的部分都达到能被覆盖的最大层数呢?

不难发现,我们可以将第一个点跟最后一个点配对,第二个点跟倒数第二配对……这是一种方法,最终答案就是 N N N 个点的坐标和 - 前 N N N 个点的坐标和。这么看来,更一般地,我们可以在第 N N N 个点和第 N + 1 N+1 N+1 个点之间划条分界线,只要每条线段都能经过这条分界线,即左边的只能和右边的配对,那么最终答案不也是 N N N 个点的坐标和 - 前 N N N 个点的坐标和了吗?

有了这个分析,我们可以想想怎么应用在括号序列中了。


如果我们把 A A A 序列排个序,如果能保证前一半的点只跟后一半的点配对,那么答案一定最大化了。我们只需要满足这个条件(并且恐怕必须得满足这个条件),就是正确答案了。

那么就可以先把 A A A 排个序(最好带上原下标),然后把前一半的变成白点,后一半的变成黑点。

在排序前的序列中,白点黑点个数相同,那么把相邻的黑白点配对,变成左括号和右括号,再把它们在序列中删掉。由于序列中白点黑点个数一直相同,因此总有白点黑点相邻。这样就可以构造出一种合法方案了。

当然,具体实现没必要这么麻烦。这就比较考验各位的模拟能力了。

CODE

#include<set>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 400005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
#define INF 0x3f3f3f3f
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
int n,m,i,j,s,o,k;
int a[MAXN];
struct it{
	int nm,id;
}b[MAXN];
bool cmp(it a,it b) {return a.nm == b.nm ? (a.id < b.id):(a.nm < b.nm);}
int main() {
	n = read();
	for(int i = 1;i <= 2*n;i ++) {
		a[i] = read();b[i].nm = a[i];b[i].id = i;
	}
	sort(b + 1,b + 1 + 2*n,cmp);
	for(int i = 1;i <= n;i ++) {
		a[b[i].id] *= -1;
	}
	int ct = 0;
	for(int i = 1;i <= 2*n;i ++) {
		if(a[i] < 0) {
			if(ct < 0) putchar(')');
			else putchar('(');
			ct ++;
		}
		else {
			if(ct > 0) putchar(')');
			else putchar('(');
			ct --;
		}
	}ENDL;
	return 0;
}
posted @ 2021-05-29 08:38  DD_XYX  阅读(39)  评论(0)    收藏  举报