Atcoder agc026E

自作多情 提交于 2019-12-10 21:18:23

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

#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;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!