题目链接: Compressed Bracket Sequence
大致题意
给定一个数字序列 a a a, 奇数位置表示有多少个左括号, 偶数位置表示有多少个右括号.
现在问有多少个连续的子段, 满足子段中的括号可以正确匹配.
解题思路
思维 (感觉这个题好难啊, 可能是我不配打ACM, 555)
我们不妨枚举 l l l, 统计以当前 l l l处的左括号为起始的合法序列数量. 这样我们可以保证答案是不重不漏的.
此时产生的贡献有两种情况: ① l l l位置的左括号与后续右括号成功匹配时的贡献. ② l l l位置开始的合法子序列与后续合法子序列拼合产生的贡献.
例如: a [ ] = { 4 , 2 , 1 , 2 } a[] = \{ 4, 2, 1, 2 \} a[]={4,2,1,2} 括号序列为: (((( )) ( ))
我们现在位于位置1处. 此时:
(((( )) ( )) 红色部分产生①类型的贡献, 贡献度为2.(((( )) ( ) ) 红色与蓝色部分产生②类型的贡献, 贡献度为1.
(((( )) ( ) ) 绿色部分产生①类型的贡献, 贡献度为1.
你可能想说, 蓝色括号部分本身也会产生①类型的贡献, 但是此时我们只枚举到 1 1 1位置, 如果此时我们去计算 3 3 3位置的答案, 不符合我们的分类标准, 从而会导致答案重复计算.
我们来分析一下什么时候需要统计这两种类型的贡献:
对于①类型: 只有当用到当前 l l l位置的左括号时, 才会产生贡献. 我们在向后扫描的过程中, 会产生非 l l l位置的左括号, 这些左括号会优先与后续右括号进行匹配, 注意这些匹配并不会在 l l l处产生①类型贡献. 如上例中的蓝色部分.
对于②类型: 产生这部分贡献, 当且仅当不存在非 l l l位置且未匹配的左括号. 此时假设在 p o s pos pos位置满足条件, 表明区间 [ l , p o s − 1 ] [l, pos - 1] [l,pos−1]或 [ l , p o s + 1 ] [l, pos + 1] [l,pos+1]都是合法的. 但是我们其实并没有记录 [ l , p o s + 1 ] [l, pos + 1] [l,pos+1]的情况, 因此会产生1的贡献.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E3 + 10;
int a[N];
int main()
{
int n; cin >> n;
rep(i, n) scanf("%d", &a[i]);
ll res = 0;
for (int l = 1; l <= n; l += 2) {
res += min(a[l], a[l + 1]);
int left = a[l] - a[l + 1];//left表示当前l位置剩余的, 未匹配的左括号数量.
ll more = 0; // 表示非l位置, 剩余的未匹配的左括号数量, 如果为负数, 表明有 -more个右括号没有匹配.
for (int r = l + 3; r <= n; r += 2) {
if (left < 0) break; //如果l位置的左括号不够了, 则退出. 注意是严格小于0, 否则会少②类型贡献.
more += a[r - 1] - a[r];
if (more < 0) res += min<ll>(-more, left), left += more, more = 0;
if (!more) res++;
}
}
cout << res << endl;
return 0;
}