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


    题目大意

    给出长度为n的排列,将其划分成单调子序列(上升or下降),满足子序列个数不超过长度为n的所有排列的划分最大值,即可以不需要把当前的划分成最优

    题解

    错误的做法:每次找出最长的上升/下降子序列,原因同只划上升序列

    像这样的数据就会挂掉:

    1
    35
    30 18 12 5 21 23 13 31 33 19 27 28 22 7 3 17 6 8 1 35 10 32 2 15 29 34 24 9 26 25 11 16 4 14 20 
    

    因为不需要最优,所以先考虑求出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
    ",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("
    ");
    		}
    	}
    }
    
  • 相关阅读:
    Linux ALSA音频库(一) 交叉编译 详细说明
    在KEIL下查看单片机编程内存使用情况
    Linux Socket
    QT报错随手记
    Cortex-M3双堆栈MSP和PSP+函数栈帧
    Linux命令
    cdev_alloc与cdev_init区别
    一些编译报错
    SFUD+FAL+EasyFlash典型场景需求分析,并记一次实操记录
    RTThread DFS文件系统使用: 基于使用SFUD驱动的SPI FLASH之上的ELM FATFS文件系统
  • 原文地址:https://www.cnblogs.com/gmh77/p/13508788.html
Copyright © 2020-2023  润新知