题目大意:给出1~n的一个排列的一个长度为m的最长上升子序列,求原排列可能的种类数。
思路:考虑在 O(nlogn) O ( n l o g n ) 的最长上升子序列的求法中,我们建立了一个单调队列去DP,由于这个队列中的元素是单调的,所以可以用二进制去表示状态,第i位为1即为在队列中,0即不在队列中。
但是这样状压并没有考虑题目所给的限制:所给的序列为总序列的一个LIS,换一句话来说,就是这个总序列的LIS的长度必须为m,且所给的这个序列必须要是顺序加入单调队列中的。
于是我们可以转换为三进制来DP,每一个数多了一种状态,0表示未选入,1表示选入了但没有加入单调队列中,2表示选入了并且加入了单调队列中,为了保证我们状态是合法的,每次进行状态转移的时候要满足一下几个条件:
1.转移前后的序列的LIS长度都不可以超过m
2.如果新添加的这个数是所给的,那么它之前的数应该已经选中了
发现满足了这两个条件之后,由于2会保证题目所给的数是顺次加入的,1会保证LIS的长度不会超过m,所以最后合法的状态的LIS的长度一定是等于m
考虑如何DP,枚举每一个序列,并且枚举这个序列的每一个子集,所表示的意义即为在单调队列中的元素,然后将这两个状态转化为3进制之后加起来就可以枚举新的要添加的数来转移了
看代码也许更好理解
#include
#include
#include
#include
#include
using namespace std;
void File(){
#ifndef ONLINE_JUDGE
freopen("bzoj3591.in","r",stdin);
freopen("bzoj3591.out","w",stdout);
#endif
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define ll long long
template<typename T>void chckmax(T &_,T __){_=_>__ ? _ : __;}
template<typename T>void chckmin(T &_,T __){_=_<__ ? _ : __;}
const int maxn=20;
const int maxs3=3*3*3*3*3*3*3*3*3*3*3*3*3*3*3+10;
const int maxs2=(1<<15)+10;
int n,m,a[maxn],id[maxn],pow3[maxn],dp[maxs3],ans;
int b[maxs2],to[maxs2][maxn],pow2[maxn];
void out(int x,int len){
if(!len)return;
out(x/3,len-1);
cout<3;
}
int main(){
File();
scanf("%d%d",&n,&m);
memset(id,-1,sizeof(id));
REP(i,0,m-1){
scanf("%d",&a[i]);
--a[i];
id[a[i]]=i;
}
pow3[0]=pow2[0]=1;
REP(i,1,15)pow3[i]=pow3[i-1]*3;
REP(i,1,15)pow2[i]=pow2[i-1]*2;
int end=pow2[n]-1;
REP(S,0,end)REP(i,0,n-1)if((pow2[i])&S)b[S]+=pow3[i];
REP(S,0,end-1){
if(__builtin_popcount(S)>m){
to[S][n]=1;
continue;
}
REP(i,0,n-1){
if(pow2[i]&S)continue;
int pos=i+1;
while(posint SS=S|(pow2[i]);
if((pow2[pos])&S)SS^=(pow2[pos]);
if(__builtin_popcount(SS)>m)continue;
to[S][i]=SS;
}
}
dp[0]=1;
REP(S,0,end-1){
for(int S0=S;;S0=(S0-1)&S){
if(dp[b[S]+b[S0]]){
if(to[S0][n])continue;
REP(i,0,n-1){
if((pow2[i])&S)continue;
if(!to[S0][i])continue;
if(id[i]>0 && (!((pow2[a[id[i]-1]])&S)))continue;
int S2=to[S0][i];
dp[b[S2]+b[S]+pow3[i]]+=dp[b[S]+b[S0]];
if(__builtin_popcount(S)==n-1){
ans+=dp[b[S]+b[S0]];
cout<<__builtin_popcount(S2)<if(!S0)break;
}
}
cout<return 0;
}