1097E. Egor and an RPG game(Dilworth定理)
阿新 • • 發佈:2020-08-15
題目大意
給出長度為n的排列,將其劃分成單調子序列(上升or下降),滿足子序列個數不超過長度為n的所有排列的劃分最大值,即可以不需要把當前的劃分成最優
題解
錯誤的做法:每次找出最長的上升/下降子序列,原因同只劃上升序列
因為不需要最優,所以先考慮求出f(n)
結論:\(f(n)=min(k)-1,k(k+1)/2>n\)
上界可以通過1,3,2,6,5,4,10,9,8,7,...來構出,手玩一下即可
每次先找出LIS,如果LIS長度>=k就直接把LIS提出來,提出來之後變成\(k(k-1)/2>n\),歸納得到次數為k-1
如果LIS長度<k,根據Dilworth定理可知 最少全集劃分=最長反鏈,對於這題用人話來說就是 最少上升子序列劃分=最長下降子序列
那麼把上升子序列當成反鏈找下降子序列的劃分,每次把最大元(沒有i<j且ai<aj)找出來即可
證明感受一下,大概是找的多不如剩下一個小的
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 low(x) ((x)&-(x)) #define ll long long //#define file using namespace std; int a[100001],ed[100001],b[100001],c[100001],C[100001],Ans[100001],pre[100001],T,n,i,j,k,l,r,mid,ans,tot,t,mx; bool bz[100001]; int main() { #ifdef file freopen("CF1097E.in","r",stdin); // freopen("CF1097E.out","w",stdout); #endif fo(i,1,100000) { while (k*(k+1)/2<=i) ++k; Ans[i]=k-1; } scanf("%d",&T); for (;T;--T) { scanf("%d",&n);memset(bz,0,n+1); fo(i,1,n) scanf("%d",&a[i]); ans=tot=0; while (tot<n) { memset(c,0,(n+1)*4);t=0; fo(i,1,n) if (!bz[i]) { if (!t) t=1,c[1]=a[i],C[1]=i,pre[i]=0; else { l=1;r=t; while (l<r) { mid=(l+r)/2; if (c[mid]<a[i]) l=mid+1; else r=mid; } if (c[l]<a[i]) ++t,++l; c[l]=a[i],C[l]=i,pre[i]=C[l-1]; } } if (t<Ans[n-tot]+1) { while (tot<n) { t=0; fo(i,1,n) if (!bz[i]) { while (t && a[i]>c[t]) --t; ++t,c[t]=a[i],C[t]=i; } ++ans; fd(i,t,1) b[++tot]=c[i],bz[C[i]]=1; ed[ans]=tot; } break; } else { ++ans;k=0; for (i=C[t]; i; i=pre[i]) b[++tot]=a[i],bz[i]=1,++k; ed[ans]=tot; } } printf("%d\n",ans); fo(i,1,ans) { printf("%d ",ed[i]-ed[i-1]); fd(j,ed[i],ed[i-1]+1) printf("%d ",b[j]); printf("\n"); } } }