題目描述
輸入格式
僅含一行,兩個正整數 n, p。
輸出格式
僅含一行,乙個非負整數,表示你所求的答案對p取餘 之後的結果。
樣例樣例輸入
4 7
樣例輸出
3
資料範圍與提示
對於 20%的資料,滿足 n≤10;
對於 40%的資料,滿足 n≤18;
對於 70%的資料,滿足 n≤550;
對於 100%的資料,滿足 3≤n≤4200,p≤10910^9109
一看似乎不太好轉移,每個數要比它兩邊的數大或小,如果直接針對每個數進行狀態轉移,不太好弄
然而我們研究一下這種序列,要按照 小 大 小...這樣,那麼如果我們在一整個序列前加乙個更大的數,變成 大 小 大 小 有成乙個合法序列了
乙個大小大小的序列可以唯一對應乙個小大小大的合法序列,比如213變為231,相當於每個數a變為n-a+1,所以小大小大的個數與大小大小的個數相同
用f[i][j]表示長度為i的序列,開頭為j的方案數 並且是大小大小
怎麼轉移?
再看看另乙個特性
對於序列中的x和x+1,只要他們不相鄰,交換後得到乙個新序列仍合法。畫一張圖比較好得出
那麼我們可以利用這一點直接從f[i][j-1]轉移到f[i][j]
長為i,第乙個數為j-1,它的第二個數一定小於j-1,也就是j一定不與j-1相鄰,那麼j與j-1交換就可以得到長為i第一位j,第二位不是j-1(比j-1小)的合法序列
要得到第二位是j-1的序列,可以將長為i-1的狀態轉移過來
找長i-1的首位是j-1 並且是 小 大 小 大 的序列,這樣直接在前面添上j就合法了
那麼這樣的序列個數其實就是長為j-1首位為(i-1)-(j-1)的序列的個數(相當與用i-1去減沒乙個數,變成小大小大)
合起來f[i][j]=f[i][j-1]+f[i-1][(i-1)-(j-1)+1]
1 #include2 #include3 #include4view codeusing
namespace
std;
5intn;6
intp;
7int f[4300][4300];8
intmain()17}
18int ans=0;19
for(int i=1;i<=n;++i)
20 ans=1ll*(ans+f[n][i])%p;
21 ans=2ll*ans%p;
22 printf("
%d\n
",ans);
23 }
題解 地精部落 DP
設 f i 表示強制第乙個是谷的合法方案數 轉移列舉乙個排列的最大值在 就把序列分成了互不相干的兩個部分,把其中 i 1 choose j 1 的數字分配給前面部分,剩下的給後面。轉移從所有可以轉移的偶數過來 winlere include include include includeusing ...
地精部落 dp
description input 僅含一行,兩個正整數 n,p。output 僅含一行,乙個非負整數,表示你所求的答案對p取餘 之後的結果。sample input 4 7sample output 3hint 對於 20 的資料,滿足 n 10 對於 40 的資料,滿足 n 18 對於 70 的...
BZOJ 地精部落
僅含一行,兩個正整數 n,p。僅含一行,乙個非負整數,表示你所求的答案對 p 取餘 之後的結果。input 4 7output 3對於 20 的資料,滿足 n 10 對於 40 的資料,滿足 n 18 對於 70 的資料,滿足 n 550 對於 100 的資料,滿足 3 n 4200,p 10 9 ...