1. 程式人生 > >【動態規劃】51nod1780 完美序列

【動態規劃】51nod1780 完美序列

巧妙的轉化;f前兩維大小開反TLE了一發……

如果一個序列的相鄰兩項差的絕對值小於等於1,那麼我們說這個序列是完美的。 給出一個有序數列A,求有多少種完美序列排序後和數列A相同。

Input

第一行一個數n(<=30000)表示完美序列的長度
第二行n個數,表示數列A(每個數<=10^9,每個數出現次數<=100)

Output

僅包含一個整數,表示可能的方案總數(對1,000,000,007取模)

 

題目分析

暑假講過的題,今天第一眼還以為是什麼玄妙計數……

因為從左到右構造不現實,於是考慮將數字從小到大構造。這樣的好處在於現在插入的$i$只和上一次$i-1$的狀態有關係。

$f[i][j][0/1][0/1]$表示處理到第$i$個數,第$i-1$個數兩兩間存空$j$個,左右兩邊分別是否有第$i-1$個數。轉移的話就是用組合數統計,記得要預處理組合數。

所以總時間複雜度$O(n*4*100)$.

 1 #include<bits/stdc++.h>
 2 const int MO = 1000000007;
 3 const int maxn = 30035;
 4 
 5 int f[maxn][103][3][3];
 6 int fac[103],C[103][103];
 7 int n,m,ans,a[maxn],t[maxn];
 8 
 9 int read()
10 { 11 char ch = getchar(); 12 int num = 0; 13 for (; !isdigit(ch); ch=getchar()); 14 for (; isdigit(ch); ch=getchar()) 15 num = (num<<1)+(num<<3)+ch-48; 16 return num; 17 } 18 int qmi(int a, int b) 19 { 20 int ret = 1; 21 while (b) 22 {
23 if (b&1) ret = 1ll*ret*a%MO; 24 a = 1ll*a*a%MO, b >>= 1; 25 } 26 return ret; 27 } 28 inline void Add(int &x, int y){x = (x+1ll*y)%MO;} 29 int main() 30 { 31 n = read(), fac[0] = 1; 32 for (int i=1; i<=n; i++) a[i] = read(); 33 for (int i=1; i<=100; i++) fac[i] = 1ll*fac[i-1]*i%MO; 34 for (int i=0; i<=100; i++) 35 for (int j=0; j<=i; j++) 36 C[i][j] = 1ll*fac[i]*qmi(1ll*fac[j]*fac[i-j]%MO, MO-2)%MO; 37 for (int i=1, j=1; i<=n; i++) 38 { 39 while (j<=n&&a[i]==a[j+1]) j++; 40 if (a[i]+1 < a[j+1]){ 41 puts("0"); 42 return 0; 43 } 44 t[++m] = j-i+1, i = j; 45 } 46 f[1][t[1]-1][1][1] = 1; 47 for (int i=1; i<m; i++) 48 for (int j=0; j<t[i]; j++) 49 for (int s1=0; s1<=1; s1++) 50 for (int s2=0; s2<=1; s2++) 51 if (f[i][j][s1][s2]) 52 for (int k=0; k<=j; k++) 53 for (int l1=0; l1<=s1; l1++) 54 for (int l2=0; l2<=s2; l2++) 55 if (k+l1+l2&&t[i+1] >= k+l1+l2) 56 Add(f[i+1][t[i+1]-k-l1-l2][l1][l2], 1ll*f[i][j][s1][s2]*C[j][k]%MO*C[t[i+1]-1][k+l1+l2-1]%MO); 57 for (int i=0; i<=100; i++) 58 for (int j=0; j<=1; j++) 59 for (int k=0; k<=1; k++) 60 Add(ans, f[m][i][j][k]); 61 printf("%d\n",ans); 62 return 0; 63 }

 

END