uoj#529. 【美團杯2020】114514
阿新 • • 發佈:2020-07-13
題目描述
題解
必須要發掘出性質才能搞,亂找找不滿
一草稿紙的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; }