BZOJ1925 [SDOI2010]地精部落
題意
給出,求出的全排列中,波動數列的個數
- 波動數列:每個點不是波峯((比相鄰的點都大)就是波谷(比相鄰的點都小)
思路
- 最高峯的兩邊一定也是波動數列
當最高峯確定的時候:
一個長度爲的波動數列可以由一個長度爲和長度爲的波動數列組合成
一個波動數列由哪些數構成並沒有關係(離散化後都一樣)
那麼我們只需要枚舉最高峯的位置即可
波動數列的最高峯必定全在奇數位或者都在偶數位
- 波動數列對稱後即爲將最高峯在奇數位和在偶數位交換
如:對稱後爲
最高峯在奇數的情況數和最高位在偶數的情況數相同
狀態
表示長度爲的波動數列個數
狀態轉移方程
我們枚舉最高峯位置爲,表示組合數選
長度爲的波動數列個數乘長度爲的波動數列個數再乘從個數中選個數的情況即可
代碼
LL dp[maxn],comb[maxn][2];
for (int i = 2; i <= n; i++) {
comb[0][i & 1] = comb[i][i & 1] = 1;//初始化組合數C[n][0]=C[n][n]=1
for (int j = 1; j < i; j++)
comb[j][i & 1] = (comb[j - 1][i & 1 ^ 1] + comb[j][i & 1 ^ 1]) % mod;
//楊輝三角形comb[i][j]=comb[i-1][j-1]+comb[i-1][j]
//滾動數組優化組合數空間,i&1表示當前的組合數,i&1^1爲上一個組合數
for (int j = 0; j < i; j += 2)
dp[i] = (dp[i] + dp[j] * dp[i - 1 - j] % mod * comb[j][i & 1 ^ 1] % mod) % mod;
//狀態轉移方程,表示最高峯在奇數位的時候
}
cout << (dp[n] << 1) % mod << '\n';
//對稱要翻倍
AC
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 4205;
LL dp[maxn],comb[maxn][2];
int main() {
ios::sync_with_stdio(false);
int n; LL mod;
cin >> n >> mod;
dp[0] = dp[1] = 1;
comb[0][0] = comb[0][1] = comb[1][1] = 1;
for (int i = 2; i <= n; i++) {
comb[0][i & 1] = comb[i][i & 1] = 1;
for (int j = 1; j < i; j++) comb[j][i & 1] = (comb[j - 1][i & 1 ^ 1] + comb[j][i & 1 ^ 1]) % mod;
for (int j = 0; j < i; j += 2) dp[i] = (dp[i] + dp[j] * dp[i - 1 - j] % mod * comb[j][i & 1 ^ 1] % mod) % mod;
}
cout << (dp[n] << 1) % mod << '\n';
}