JZOJ 5600. 【NOI2018模拟3.26】Arg
生活随笔
收集整理的這篇文章主要介紹了
JZOJ 5600. 【NOI2018模拟3.26】Arg
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Description
給出一個長度為 m 的序列 A, 請你求出有多少種 1…n 的排列, 滿足 A 是它的一個 LIS.
Input
第一行兩個整數 n,m.
接下來一行 m 個整數, 表示 A.
Output
一行一個整數表示答案.
Sample Input
5 3
1 3 4
Sample Output
11
Data Constraint
對于前 30% 的數據, n ≤ 9;
對于前 60% 的數據, n ≤ 12;
對于 100% 的數據, 1 ≤ m ≤ n ≤ 15.
Solution
考慮計算 LIS 時使用的經典方法。
記一個數組 f ,f[i] 表示當前長度為 i 的單調上升序列的結尾的最小值。
這個數組是一個單調不降的數組. 于是我們可以用二進制來表示它。
設 f(S,S0) 表示當前選了集合 S 里的數,LIS 的 f 數組狀態為 S0 時的方案數。
這不難轉移,枚舉在末尾加上哪個數即可。
觀察發現 S0 一定屬于 S,所以可以壓成三進制。
每位用 0,1,2 分別表示還沒選、選了且在單調上升序列、選了但不在單調上升序列。
于是枚舉狀態,算出目前的單調上升序列,在枚舉還沒選的轉移即可。
注意枚舉轉移的數要按照 A 序列的順序來。
若枚舉到某一狀態,其中每個數都已經選了,且單調上升序列的長度就是 m ,即可統計答案。
時間復雜度 O(3N?N) ,實際小很多,因為很多狀態都遍歷不到。
Code
#include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespace std; int n,m,ans; int a[17],b[17],c[17],p[17],f[14348907+5]; bool bz[17]; inline int read() {int X=0,w=0; char ch=0;while(!isdigit(ch)) w|=ch=='-',ch=getchar();while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X; } int main() {n=read(),m=read();for(int i=1;i<=m;i++){b[i]=read();if(b[i]<=b[i-1] || b[i]<1 || b[i]>n) return 0&printf("0");}for(int i=p[0]=1;i<=n;i++) p[i]=p[i-1]*3;f[0]=1;for(int i=0;i<p[n];i++)if(f[i]){bool pd=true;int tot=0,x=i;for(int j=n-1;j>=0;j--){c[j+1]=x/p[j];if(x>=p[j]){if(c[j+1]==1) a[++tot]=j+1;x%=p[j];}else pd=false;}reverse(a+1,a+1+tot);if(pd && tot==m) ans+=f[i];memset(bz,false,sizeof(bz));int num=1;while(num<=m && c[b[num]]) num++;while(num+1<=m) bz[b[++num]]=true;for(int j=1;j<=n;j++)if(!c[j] && !bz[j]){if(j>a[tot]){if(tot==m) continue;f[i+p[j-1]]+=f[i];}else{int y=upper_bound(a+1,a+1+tot,j)-a;f[i+p[j-1]+p[a[y]-1]]+=f[i];}}}printf("%d",ans);return 0; }總結
以上是生活随笔為你收集整理的JZOJ 5600. 【NOI2018模拟3.26】Arg的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 4238. 【五校联考5day
- 下一篇: JZOJ 5603. 【NOI2018模