NOIP模擬 上升序列
阿新 • • 發佈:2018-10-05
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 #defineM 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模擬 上升序列