uoj#529. 【美团杯2020】114514

题目描述


题解

必须要发掘出性质才能搞,乱找找不满

一草稿纸的114514越看越草

发现5只出现一次,而且14是114的子串

显然可以想到每次按最近的来匹配,一次找出一个114514

发现114511451414会挂掉,原因是把第二个5的114给拆掉了

1的数量最多,所以只考虑总量不考虑单独每个

对比一下4的两种用法,14必须要在5的后面并且少一个1,114没有限制并且多一个1

那么14即有限制又更优,所以肯定是能匹配14就匹配

然后就回到了上面的错误做法

考虑哪里会挂掉,每个5前面必须要留一个4,所以只需要保证匹配完14后每个5都能匹配一个4即可

把4当成+1,5当成-1,前缀和就是可以匹配14(选1145不改变前缀和)的数量,用队列即可求得限制,当一个5被选的时候判断是否要弹掉队头

找数用并查集,时间复杂度O(nα)

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 ll long long
//#define file
using namespace std;

const int b[6]={0,0,1,2,0,1};
int fa[3][600002],ans[100001][6],D[600002][2],Q,n,m,i,j,k,l,s1,s2,s3,h,t,H,T,s;
char st[600001];

int gf(int T,int t)
{
	static int d[600002],tot,i;
	tot=0;
	while (fa[T][t]!=t) d[++tot]=t,t=fa[T][t];
	fo(i,1,tot) fa[T][d[i]]=t;
	
	return t;
}

int main()
{
	#ifdef file
	freopen("uoj529.in","r",stdin);
//	freopen("uoj529.out","w",stdout);
	#endif
	
	scanf("%d",&Q);
	for (;Q;--Q)
	{
		scanf("%s",st+1);n=strlen(st+1);m=n/6;
		h=1;t=0;
		
		fa[0][0]=fa[1][0]=fa[2][0]=1;
		fa[0][n+1]=fa[1][n+1]=fa[2][n+1]=n+1;
		s1=s2=s3=n+1;
		fd(i,n,1)
		{
			switch (st[i])
			{
				case '1':{s1=i;break;}
				case '4':{s2=i;break;}
				case '5':{s3=i;break;}
			}
			fa[0][i]=s1,fa[1][i]=s2,fa[2][i]=s3;
		}
		
		l=0;H=1;T=0;
		fo(i,1,n)
		if (st[i]=='4') ++l;
		else
		if (st[i]=='5')
		{
			--l;
			while (T && D[T][1]>=l) --T;
			++T;D[T][0]=i;D[T][1]=l;
		}
		
		s=0;D[++T][1]=n;D[T][0]=0;
		while (h<=m)
		{
			if (h<=t && s<D[H][1])
			{
				j=ans[h][3];
				fo(k,4,5) j=gf(b[k],j),ans[h][k]=j,fa[b[k]][j]=j+1,++j;
				++h,++s;
			}
			else
			{
				++t,j=0;
				fo(k,0,3) j=gf(b[k],j),ans[t][k]=j,fa[b[k]][j]=j+1,++j;
				if (D[H][0]==ans[t][3]) ++H;
			}
		}
		fo(i,1,m) {fo(j,0,5) printf("%d ",ans[i][j]);printf("\n");}
	}
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2020-07-13 11:07  gmh77  阅读(262)  评论(0编辑  收藏  举报