靖爷的仙仙题,其实并不难,可是想了挺久,码了挺久,调了挺久,本人菜鸡了挺久,还是不够专注
就是问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 ",ans); return 0; }