1. 程式人生 > >NOIP模擬 上升序列

NOIP模擬 上升序列

style void 轉移 print nbsp 使用 當前 備註 表示

【題目描述】

給出一個長度為 m 的上升序列 A(1 ≤ A[i]≤ n), 請你求出有多少種 1...n 的排列, 滿足 A 是它的一個 LIS.

【輸入格式】

第一行兩個整數 n,m.

接下來一行 m 個整數, 表示 A.

【輸出格式】

一行一個整數表示答案.

【樣例輸入】

5 3

1 3 4

【輸出格式】

11

【備註】

對於前 30% 的數據, n ≤ 9;

對於前 60% 的數據, n ≤ 12;

對於 100% 的數據, 1 ≤ m ≤ n ≤ 15.

【題目分析】

一看這個數據範圍,覺得像狀壓DP,但打死想不出壓什麽,打了個深搜。。。滾粗。。。

最長不降子序列的一種求法是:使用附加數組D[i],表示當前長度為 i的最長不降子序列最小的結尾是多少,顯然它是單調遞增的。每插入一個數就二分找到第一個大於它的位置替換。

我們可以設狀態S1,第i位為 1表示它在D 中出現了,出現的位置就是1~i位1 的數量

現在就可以設遞推狀態f(S,S1)表示當前用了S中的數,D的狀態為S1的方案數

轉移很容易轉移,枚舉這一位選什麽直接更新

註意到S1一定是S的子集,於是可以用三進制表示

復雜度O(3^n? n)實際上遠遠不到

【代碼~】

#include<bits/stdc++.h>   
#define fo(i,a,b) for(int i=a;i<=b;++i)   
#define fod(i,a,b) for(int i=a;i>=b;--i)   
#define N 16   
#define
M 14348910 #define LL long long using namespace std; int n,m,cf[N],a[N],wz[N],cf2[N]; LL f[M],ans; void dfs(int k,int lim,int v) { if(k>n) { if(lim==0) ans+=f[v]; return; } v+=cf[k-1]; dfs(k+1,lim,v); v+=cf[k-1]; dfs(k
+1,lim-1,v); } int main() { cin>>n>>m; fo(i,1,m) scanf("%d",&a[i]),wz[a[i]]=i; cf[0]=cf2[0]=1; fo(i,1,n) cf[i]=cf[i-1]*3,cf2[i]=cf2[i-1]*2; f[0]=1; fo(i,0,cf2[n]-2) { bool pd=1,c1=1; fo(j,1,m) { if((i&cf2[a[j]-1])==0) pd=0; else if(!pd) { c1=0; break; } } if(c1) { for(int i1=i;1;i1=(i1-1)&i) { int vi=0; fo(j,1,n) { if(i1&cf2[j-1]) vi+=cf[j-1]; if(i&cf2[j-1]) vi+=cf[j-1]; } if(f[vi]) fo(j,1,n) { if(vi/cf[j-1]%3==0) { int v=vi+2*cf[j-1]; fo(p,j+1,n) if(v/cf[p-1]%3==2) { v-=cf[p-1]; break; } f[v]+=f[vi]; } } if(i1==0) break; } } } dfs(1,m,0); printf("%lld\n",ans); return 0; }

NOIP模擬 上升序列