1. 程式人生 > >【bzoj1925】[Sdoi2010]地精部落 組合數學+dp

【bzoj1925】[Sdoi2010]地精部落 組合數學+dp

多少 rdquo 工作 左右 ash con sdoi2010 tdi dash

題目描述

傳說很久以前,大地上居住著一種神秘的生物:地精。 地精喜歡住在連綿不絕的山脈中。具體地說,一座長度為 N 的山脈 H可分 為從左到右的 N 段,每段有一個獨一無二的高度 Hi,其中Hi是1到N 之間的正 整數。 如果一段山脈比所有與它相鄰的山脈都高,則這段山脈是一個山峰。位於邊 緣的山脈只有一段相鄰的山脈,其他都有兩段(即左邊和右邊)。 類似地,如果一段山脈比所有它相鄰的山脈都低,則這段山脈是一個山谷。 地精們有一個共同的愛好——飲酒,酒館可以設立在山谷之中。地精的酒館 不論白天黑夜總是人聲鼎沸,地精美酒的香味可以飄到方圓數裏的地方。 地精還是一種非常警覺的生物,他們在每座山峰上都可以設立瞭望臺,並輪 流擔當瞭望工作,以確保在第一時間得知外敵的入侵。 地精們希望這N 段山脈每段都可以修建瞭望臺或酒館的其中之一,只有滿足 這個條件的整座山脈才可能有地精居住。 現在你希望知道,長度為N 的可能有地精居住的山脈有多少種。兩座山脈A 和B不同當且僅當存在一個 i,使得 Ai≠Bi。由於這個數目可能很大,你只對它 除以P的余數感興趣。

輸入

僅含一行,兩個正整數 N, P。

輸出

僅含一行,一個非負整數,表示你所求的答案對P取余 之後的結果。

樣例輸入

4 7

樣例輸出

3


題解

自己yy的組合數學+dp

首先我們可以思考,一個序列中,“1”所在的位置一定是山谷。那麽“1”左側一定是右面為山峰,“1”右側一定是左面為山峰。

然後我們可以發現左右是山峰的情況是對稱的,所以相當於一邊為山峰的情況。

而且左右互不影響,是相同的子問題。

所以對答案的貢獻為“1”左邊一邊為山峰的方案數*“1”右邊一邊為山峰的方案數*從n-1個中選出“1”左邊個數的數的方案數(組合數)。

同理可以更新出一邊為山峰的方案數、兩邊為山峰的方案數。

狀態轉移方程(a[i]表示i個數的方案數,b[i]表示i個數中一邊(不嚴格)為山峰的方案數,c[i]表示i個數中兩邊為山峰的方案數):

$a[i]=\sum\limits_{j=1}^ib[j-1]·b[i-j]·C_{i-1}^{j-1}\\b[i]=\sum\limits_{j=1}^ib[j-1]·c[i-j]·C_{i-1}^{j-1}\\c[i]=\sum\limits_{j=1}^ic[j-1]·c[i-j]·C_{i-1}^{j-1}$

dp初值什麽的看著辦就好了。

然後就可以O(n^2)求出答案啦。

常數巨大。。。網上很多題解是分奇偶性討論的,常數可能會小一些。

註:題目不保證p是質數,所以需要遞推組合數,這會導致MLE,需要使用滾動數組。

#include <cstdio>
#define N 4210
typedef long long ll;
ll a[N] , b[N] , c[N];
int k[2][N];
int main()
{
	int n , p , i , j;
	scanf("%d%d" , &n , &p);
	a[0] = b[0] = a[1] = b[1] = c[1] = k[1][0] = 1;
	for(i = 2 ; i <= n ; i ++ )
	{
		k[i & 1][0] = 1;
		for(j = 1 ; j <= i ; j ++ ) k[i & 1][j] = (k[(i & 1) ^ 1][j - 1] + k[(i & 1) ^ 1][j]) % p;
		for(j = 1 ; j <= i ; j ++ )
		{
			a[i] = (a[i] + k[i & 1][j - 1] * b[j - 1] % p * b[i - j] % p) % p;
			b[i] = (b[i] + k[i & 1][j - 1] * c[j - 1] % p * b[i - j] % p) % p;
			c[i] = (c[i] + k[i & 1][j - 1] * c[j - 1] % p * c[i - j] % p) % p;
		}
	}
	printf("%lld\n" , a[n]);
	return 0;
}

【bzoj1925】[Sdoi2010]地精部落 組合數學+dp