AGC026E

题目大意

一个长度2n的ab各n个的ab串,每次可以同时删第i个a和第i个b,求剩下的最大字典序的串

n<=3000

题解

把a当作+1b当作-1,在前缀和为0的位置划开变成若干段,那么删只会影响到段的内部

结论:在同一个段内,每个ab对(x,y)的x和y的大小关系相同,否则一定会经过前缀和为0的位置

把以a开头的称为A段,b开头的称为B段,分类讨论

对于一个A段,如果后面有B段就全删掉最优

那么有效的A段是最大全A后缀,并且剩下的一定是abababab...的形式最优,因此贪心选即可

对于一个B段,设第一个选的组i为(x,y),那么y+1~x-1中的b一定全选最优,于是又会往后选一些a,如此选下去最后一定会把>=i的组全选掉(因为只有结尾处前缀和为0,如果在中间停了就矛盾了)

所以只有len/2+1种选法,暴力枚举判断即可

之后从后往前贪心选择合并,时间复杂度O(n^2)

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define ll long long
//#define file
using namespace std;

struct type{
	char a[6001];
	int n;
	void add(char ch) {a[++n]=ch;}
} ans,s,Ans;
int a[6001][3],c[6001][2],n,i,j,k,l,sum,tot,s1,s2;
char st[6001];
bool bz;

bool pd(type a,type b)
{
	int i;
	
	fo(i,1,min(a.n,b.n))
	if (a.a[a.n-i+1]!=b.a[b.n-i+1])
	return a.a[a.n-i+1]<b.a[b.n-i+1];
	return a.n<b.n;
}

int main()
{
	#ifdef file
	freopen("agc026e.in","r",stdin);
	#endif
	
	scanf("%d",&n);
	scanf("%s",st+1);
	
	fo(i,1,n+n)
	{
		if (!sum)
		{
			++tot;
			a[tot][0]=i;
			a[tot][2]=st[i]=='b';
		}
		sum+=(st[i]=='a')?1:-1;
		if (!sum) a[tot][1]=i;
	}
	
	fd(l,tot,1)
	if (!a[l][2])
	{
		if (!bz)
		{
			s1=s2=0;
			fo(i,a[l][0],a[l][1])
			{
				if (st[i]=='a')
				++s1,c[s1][0]=i;
				else
				++s2,c[s2][1]=i;
			}
			
			k=(a[l][1]-a[l][0]+1)/2,j=0;
			fo(i,1,k)
			if (j<c[i][0])
			ans.add('b'),ans.add('a'),j=c[i][1];
		}
	}
	else
	{
		bz=1;Ans=ans;
		fd(i,(a[l][1]-a[l][0]+1)/2,0)
		{
			s=ans;
			s1=s2=0;
			fd(j,a[l][1],a[l][0])
			{
				if (st[j]=='a')
				{++s1;if (s1<=i) s.add('a');}
				else
				{++s2;if (s2<=i) s.add('b');}
			}
			if (pd(Ans,s)) Ans=s;
		}
		ans=Ans;
	}
	
	fd(i,ans.n,1) putchar(ans.a[i]);
	putchar('
');
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/13627997.html