1097E. Egor and an RPG game(Dilworth定理)

题目大意

给出长度为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");
		}
	}
}
上一篇:2018NOIP提高组Day1(铺设道路&货币系统&赛道修建)


下一篇:最大流dinic算法