Atcoder agc026E

63 篇文章 0 订阅
27 篇文章 1 订阅

对于第 i i i组匹配的 a a a b b b,我们可以将按位置将它们称为 ( p i , q i ) (p_i,q_i) (pi,qi)
将原序列中的 a a a看做 1 1 1 b b b看做 − 1 -1 1,那么前缀和为 0 0 0的位置就将序列分为了若干段。显然每组 p i p_i pi q i q_i qi都在同一段中,且同一段中相应的 p i p_i pi q i q_i qi的大小关系不变。我们称 p i > q i p_i>q_i pi>qi的段为 A A A类,否则为 B B B类。
考虑对于一个 A A A类的段,我们其中选择最早的是第 i i i组,那么可以删去所有 < i <i <i的组。由于 p i > q i p_i>q_i pi>qi,因此相应的 a a a b b b后面,并且易知 p i p_i pi q i q_i qi之间没有被删去的都是某一组开头的 b b b。为了字典序尽量大,我们显然会将这些 b b b全选,于是又会选后面的若干个 a a a。这样推下去我们会将该段内所有 ≥ i \geq i i的组都选了(因为中间前缀和不为 0 0 0,所以刚刚的过程在段尾之前不会结束)。于是对于一个长度为 l e n len len A A A类段,我们只有 l e n / 2 len/2 len/2种可能的选法,分别暴力求出来即可。
对于一个 B B B类的段,我们如果在其中选了某些组,那么开头一定是 a a a。所以如果后面有 A A A类段我们一定不会选,否则我们的选择显然形如 a b a b a b . . . ababab... ababab...,且会让 a b ab ab尽量多,这个贪心选择最前面能选的即可。
最后简单从后往前合并一下每段答案即可。时间复杂度 O ( N 2 ) \mathcal O(N^2) O(N2)

#include <bits/stdc++.h>

using namespace std;

string str,f[3005];
int pos[3005];
bool kind[3005];

int bel[6005];

int main() {
  int n;
  cin>>n>>str;
  int cnt=0,s=0;
  for(int i=0;i<2*n;i++) {
  	s+=((str[i]=='b')?1:-1);
  	if (!s) {
  		pos[++cnt]=i;
  		kind[cnt]=((str[i]=='a')?0:1);
	  }
  }
  pos[0]=-1;
  bool v=1;
  for(int i=cnt;i>0;i--)
    if (!kind[i]) {
    	v=0;
    	int s1=0,s2=0;
    	for(int j=pos[i-1]+1;j<=pos[i];j++)
    	  if (str[j]=='b') bel[j]=++s1; else bel[j]=++s2;
    	f[i]=f[i+1];
    	for(int j=1;j<=s1;j++) {
          string cur;
    	  for(int k=pos[i-1]+1;k<=pos[i];k++)
    	    if (bel[k]>=j) cur+=str[k];
    	  f[i]=max(f[i],cur+f[i+1]);
    	}
	}
	else if (v) {
		int s1=0,s2=0;
		for(int j=pos[i-1]+1;j<=pos[i];j++)
    	  if (str[j]=='a') bel[j]=++s1; else bel[j]=++s2;
    	int r=pos[i-1]+1;
    	for(;;) {
    		while (r<=pos[i]&&str[r]=='b') r++;
    		if (r>pos[i]) break;
    		f[i]+="ab";
    		int t=bel[r];
    		r++;
    		while (bel[r]!=t) r++;
    		r++;
		}
		f[i]+=f[i+1];
	}
	else f[i]=f[i+1];
  cout<<f[1]<<endl;
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值