Educational Codeforces Round 166 (Rated for Div. 2)D. Invertible Bracket Sequences 二分+ST表

Problem - D - Codeforces

我们做出' ( '的前缀和数组bal[ ],其中' ) '是-1

对于第四个样例: 

(()())(())

我们可以看到,正则的括号序列的前缀是相等的——最开始的前一个位置和结束都为0. bal[n] == 0

内部的正则括号序列前缀也相等——bal[ l-1 ] == bal[ r ]

我们选择反转的子序列必须也是正则,只有这样bal[n]才会仍是0.也就是右边仍能匹配住,承受住

左边能否承受住?对于我们选择的序列往左,有bal[ l-1 ]个左括号可以来承担,所以我们序列中的左括号 bal[ i ] - bal[ l-1 ] 不能比之大。(因为我们反转过去的是左括号)

如同题解所说:

此时暴力解法应该可以写出。

对于区间有无不合法前缀,变换下式子,bal[ l-1 ]*2 >= bal[ i ] ,因此我们只需要知道最大值是否不超过,这个可以用ST表来查询。

而且只要最大值不超过即可,那么我们二分快速找哪个位置不符合即可。

时间复杂度:O(logn + nlogn)

个人代码参考:

Submission #263880074 - Codeforces

#define endl "\n"
#define PII pair<int,int>
#define int long long

const int maxn = 2e5 + 5;
int arr[maxn];
int pre[maxn];
int st[maxn][22];//2^21 == 2e6          

void solve()
{
	string str;
	cin >> str;
	str = ' ' + str;
	int n = str.size();
	map<int, vector<int>>mm;
	for (int i = 1; i < n; i++)
	{
		if (str[i] == ')')
			pre[i] = -1;
		else
			pre[i] = 1;
		pre[i] += pre[i - 1];
		mm[pre[i]].push_back(i);
	}

	for (int i = 1; i < n; i++)
		st[i][0] = pre[i];
	for (int p = 1; p < 22; p++)
	{
		int len = pow(2, p);
		for (int i = 1; i + len <= n; i++)
			st[i][p] = max(st[i][p - 1], st[i + len / 2][p - 1]);
	}


	int ret = 0;
	for (auto& x : mm)
	{
		vector<int>arr = x.second;
		for (int i = 0; i < arr.size(); i++)
		{
			int left = arr[i];
			int l = i, r = arr.size() - 1;
			while (l < r)
			{
				int mid = (l + r+1) / 2;
				int c = arr[mid];
				int p = log2(c - left);
				if (max(st[left + 1][p], st[(int)(c - pow(2, p) + 1)][p]) <= pre[left] * 2)
					l = mid;
				else
					r = mid - 1;
			}
			ret += l-i;
		}
	}
	cout << ret << endl;
}

官方题解:

非常短。

我们不关心位置的话,可以用map统计每个前缀有多少个。

对于当前位置,统计自己合法数目。

同时删掉自己造成的不合法的左前缀:

对于每个当前值,我们删掉不合法的左边界。

因为此后当前位置都是这个左边界形成的边界之内,都不合法了。

#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
  int t;
  cin >> t;
  while (t--) {
    string s;
    cin >> s;
    map<int, int> cnt;
    int b = 0;
    ++cnt[b];
    long long ans = 0;
    for (auto& c : s) {
      b += (c == '(' ? +1 : -1);
      ans += cnt[b];
      ++cnt[b];
      while (cnt.begin()->first * 2 < b)
        cnt.erase(cnt.begin());
    }
    cout << ans << '\n';
  }
}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值