bzoj3591: 最长上升子序列

靖爷的仙仙题,其实并不难,可是想了挺久,码了挺久,调了挺久,本人菜鸡了挺久,还是不够专注

就是问n的排列中,最长上升子序列长度为k的方案数,同时有一些约束

不难想到把求lis的单调栈压下来,但是还要压一个这个数字用了没有,状态数就是2^2n的了。感觉这个做法不对然后就想歪了

其实可以把这个东西压成三进制的,0不存在,1存在且在单调栈,2存在且已出单调栈,注意1,2不能调换,不然枚举顺序就出问题了

详情见代码。卡常没卡过

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int _=1e2;
const int maxn=15+2;
const int fbin=(1<<15)+_;
const int tbin=14348907+_;
int n,li;

int m,num[maxn],rk[maxn],sf[fbin];
void getsf()
{
    for(int i=1;i<=m;i++)rk[num[i]]=i;
    for(int zt=0;zt<li;zt++)
    {
        sf[zt]=m+1;
        for(int i=1;i<=m;i++)
            if(!(zt&(1<<num[i]-1))){sf[zt]=i;break;}
    }
}

int dl[maxn][fbin],o[fbin];//单调栈加入i时,踢那个点 
void getdl()
{
    for(int zt=0;zt<li;zt++)
    {
        int p=0;
        for(int i=n;i>=1;i--)
            if(zt&(1<<i-1))p=i;
            else dl[i][zt]=p;
        o[zt]=o[zt>>1]+(zt&1);
    }
}

int mi[maxn],f[tbin];//0不存在,1存在且在单调栈,2存在且已出单调栈
void divi(int zt,int &xx,int &yy)//该三进制是由(这个数是否存在)(这个数是否在单调栈里)两个二进制压缩而成的
{
    xx=0,yy=0;
    for(int i=1;i<=n;i++)
    {
        int d=zt/mi[i-1]%3;
        if(d>=1)xx|=(1<<i-1);
        if(d==1)yy|=(1<<i-1);
    }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);li=(1<<n);
    mi[0]=1;for(int i=1;i<=n;i++)mi[i]=mi[i-1]*3;
    if(m==1){puts("1");return 0;}
    
    for(int i=1;i<=m;i++)scanf("%d",&num[i]);
    getsf();
    getdl();
    
    int ans=0;f[0]=1;
    for(int zt=0;zt<mi[n];zt++)
        if(f[zt])
        {
            int xt,yt;
            divi(zt,xt,yt);    if(xt==li-1&&o[yt]==m)ans+=f[zt];
            for(int i=1;i<=n;i++)
                if(!(xt&(1<<i-1)) && rk[i]<=sf[xt])
                    f[zt+mi[dl[i][yt]-1]+mi[i-1]]+=f[zt];
        }
    printf("%d\n",ans);
    
    
    return 0;
}

 

 

 

posted @ 2019-05-12 17:08  AKCqhzdy  阅读(300)  评论(0编辑  收藏  举报