BZOJ1925: [Sdoi2010]地精部落 組合數+DP

BZOJ1925 [SDOI2010]地精部落


題意

給出nn,求出nn的全排列中,波動數列的個數(0<n<4201)(0<n<4201)

  • 波動數列:每個點不是波峯((比相鄰的點都大)就是波谷(比相鄰的點都小)

思路

  • 最高峯的兩邊一定也是波動數列
    當最高峯ii確定的時候:
    一個長度爲nn的波動數列可以由一個長度爲i1i-1和長度爲n1in-1-i的波動數列組合成
    一個波動數列由哪些數構成並沒有關係(離散化後都一樣)

那麼我們只需要枚舉最高峯的位置即可
波動數列的最高峯必定全在奇數位或者都在偶數位

  • 波動數列對稱後即爲將最高峯在奇數位和在偶數位交換
    如:3,1,4,2{3,1,4,2}對稱後爲2,4,1,3{2,4,1,3}
    最高峯在奇數的情況數和最高位在偶數的情況數相同

狀態

dp[i]dp[i]表示長度爲ii的波動數列個數

狀態轉移方程

我們枚舉最高峯位置爲jj,comb[n][m]comb[n][m]表示組合數nnmm
dp[i]=j=0n1dp[j]dp[n1j]comb[n1][j]dp[i]=\sum_{j=0}^{n-1} dp[j]*dp[n-1-j]*comb[n-1][j]

長度爲jj的波動數列個數乘長度爲n1jn-1-j的波動數列個數再乘從n1n-1個數中選jj個數的情況即可


代碼

dpdp

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';
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章