【bzoj1925】[Sdoi2010]地精部落 組合數學+dp
題目描述
傳說很久以前,大地上居住著一種神秘的生物:地精。 地精喜歡住在連綿不絕的山脈中。具體地說,一座長度為 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