哎我还是太菜了,这么短的代码 断断续续 调了好久。
看数据范围 大概就是状压
DP
题目名字是【最长上升子序列】 可以猜测是拿跑
LIS
的单调栈搞搞。
那么可以设计一个状态
f[A][B]
表示已选集合
A
,其中
转移大概就是枚举
A
补集中的每个元素
还有一个不知道有多大用 的常数优化。就是限制一下
B
的大小使其不超过
#include <bits/stdc++.h>
using namespace std;
const int N=15;
int n,m,S,T,pre[N],bit[1<<N],pow3[N],g[1<<N][N],d[1<<N][N],f[14348908],h[1<<N];
int main(){
//freopen("aa.in","r",stdin);
int i,j,k,x,y;
scanf("%d%d",&n,&m);
for(y=-1,i=0;i<m;++i){
scanf("%d",&x);--x;
T|=(1<<x);pre[x]=y;y=x;
}
for(pow3[0]=i=1;i<n;++i)pow3[i]=pow3[i-1]*3;
S=(1<<n)-1;
for(i=0;i<=S;++i)bit[i]=bit[i>>1]+(i&1);
for(i=0;i<=S;++i)for(j=0;j<n;++j)if(1<<j&i)h[i]+=pow3[j];
for(i=0;i<=S;++i)for(j=0;j<n;++j)if(!(1<<j&i)){
if(!((1<<j)&T))g[i][j]=1;
else{
for(k=pre[j];~k;k=pre[k])if(!(1<<k&i))break;
if(k<0)g[i][j]=1;
}
if(bit[((1<<(j+1))-1)&i]<m){
for(k=1<<(j+1);k<=i&&!(k&i);k<<=1);
d[i][j]=1<<j|i;
if(k<=i)d[i][j]^=k;
}
}
f[0]=1;
for(i=0;i<=S;++i)for(k=0;k<n;++k)if(g[i][k]){
for(j=i;;j=(j-1)&i){
if(f[h[i]+h[j]]&&d[j][k])f[h[1<<k|i]+h[d[j][k]]]+=f[h[i]+h[j]];
if(!j)break;
}
}
int ans=0;
for(i=1;i<=S;++i)if(bit[i]<=m)ans+=f[h[S]+h[i]];
printf("%d",ans);
return 0;
}