[单调队列][前缀和][滑窗][Codeforces] Round #594 (Div. 2) D1 The World Is Just a Programming Task

问题:给定一括号序列,问交换任意一对括号后,使序列任意循环滚动,这len种滚动串最大有多少个合法序列

首先若左括号不等于右括号必死无疑。

剩下考虑枚举交换,对于相同括号交换后没意义,out

对于不同括号交换后,假设序列已经合法,则滚动合法情况数就等于序列中满匹配的括号序列数

定义满匹配:

对于序列(())(()())满匹配有两个 1234 和 5678910,将这两个子串拆出来,前面后面没有待匹配的括号称为满匹配

这样滚动时每个满匹配完全滚动到序列最后时必然会形成一种合法新情况

然后考虑如何对一个交换序列找到一种合法序列情况去找满匹配

对序列*2(滚动)后做前缀和,左括号1右括号-1,若某位置为0则在此位置前括号序列匹配合法 负数必然不合法

然后考虑枚举一个滑窗 每次去掉一个括号,则对下一位开始的长度为len的影响是都+1 -1(前缀和性质)

当我去掉括号并使得下一位开始长度为len的滑窗中的最小值变为0 则必然合法(废话)

所以对于每个位置,我只需要用单调队列维护下一个位置开始长度为len的滑窗的最小值+前面去掉的贡献判断是否为0即可

若是0则当前位置开始的序列必然合法,然后维护序列满匹配的值更新答案即可。

时间复杂度O(n^3) 考虑本题实际复杂度为250 * 250 * 1000 六千二百五十万,639ms卡过了。。。

/*
    Zeolim - An AC a day keeps the bug away
*/
 
//#pragma GCC optimize(2)
//#pragma GCC ("-W1,--stack=128000000")
#include <bits/stdc++.h>
using namespace std;
#define mp(x, y) make_pair(x, y)
#define fr(x, y, z) for(int x = y; x < z; ++x)
#define pb(x) push_back(x)
#define mem(x, y) memset(x, y, sizeof(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef std::pair <int, int> pii;
typedef std::vector <int> vi;
//typedef __int128 ill;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 386910137;
const ull P = 13331; 
const int MAXN = 1e3 + 100;
 
int ans, al, ar, len;
 
int arr[MAXN] = {0};
int rmin[MAXN] = {0};
int getpos(string s)//找到循环串的第一个合法匹配位置
{
	int ret = 0;
	for(int i = 0; i < len * 2; ++i)
	{
		if(s[i % len] == '(') ++ret;
		else --ret;
		arr[i] = ret;
	}
	deque <int> Q;
	for(int i = 0; i < len * 2; ++i) //单调队列维护区间极小值
	{
		while(!Q.empty() && Q.front() <= i - len)
			Q.pop_front();
		if(Q.empty() || arr[i] < arr[Q.front()] ) Q.push_front(i);
		else if(arr[i] > arr[Q.back()]) Q.push_back(i);
		if(i >= len - 1) rmin[i - len + 1] = arr[Q.front()]; 
	}
	if(rmin[0] == 0) return 0;
	for(int i = 0; i < len; ++i) //判断将i位循环到最后时是否合法
	{
		if(s[i] == '(') --ret;
		else ++ret;
		if(ret + rmin[i + 1] == 0) return i + 1;
	}
	return -1;
}
int get(string s, int cl, int cr) //查找满匹配串
{
	swap(s[cl], s[cr]);
	deque <char> Q;
	for(int i = 0; i < len; ++i)
		Q.push_back(s[i]);
	int ret = 0;
	int k = getpos(s);
	if(k == -1)
		return 0;
	while(k--) //将串循环到第一个合法情况
	{
		Q.push_back(Q.front());
		Q.pop_front();
	}
	int fst = 0, lst = 0;
	while(!Q.empty())
	{
		if(Q.front() == '(')
			++fst;
		else
			++lst;
		Q.pop_front();
		if(lst > fst)
			return 0;
		if(fst == lst) //满匹配了 计数重置
		{
			++ret;
			fst = 0, lst = 0;
		}
	}
	
	return ret;
}
 
int main()
{  
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    //freopen("d:\out.txt","w",stdout);
    //freopen("d:\in.txt","r",stdin);
    
    cin >> len;
    
    string s, b;
    
    cin >> s;
    
    int fst = 0, lst = 0;
    
    for(int i = 0; i < len; ++i)
    	if(s[i] == '(') ++fst;
    	else ++lst;
    	
    if(fst != lst)
    {
    	cout << "0\n1 1\n";
    	return 0;
	}
    
    ans = get(s, 0, 0);
    
    for(int i = 0; i < len; ++i)
    {
    	for(int j = i + 1; j < len; ++j)
    	{
    		if(s[i] != s[j])
    		{
    			int x = get(s, i, j);
    			if(x > ans)
    				ans = x, al = i, ar = j;
			}
		}
	}
	
	cout << ans << '\n';
	
	cout << al + 1 << ' ' << ar + 1 << '\n';
    
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值