#線性dp#洛谷 5999 [CEOI2016]kangaroo
阿新 • • 發佈:2021-12-17
線性dp
題目
問有多少個長度為 \(n\) 的排列滿足首項為 \(st\),末項為 \(ed\),
並且 \(\forall i\in (1,n),\left[a_{i-1}<a_i \oplus a_i<a_{i+1}\right]\),\(\oplus\) 表示異或
分析
這道題提供了一個求合法排列方案數的一種新方法。
設 \(dp[i][j]\) 表示前 \(i\) 個數,分成 \(j\) 段合法序列的方案數。
如果 \(i==st \or i==ed\) 那麼要麼新開一段或者直接併到左側或右側,也就是
\(dp[i][j]=dp[i-1][j-1]+dp[i-1][j]\)。
否則可以將這個當前最大數插入在兩段之間,或者新開一段,但是首尾可能放不了,要減掉,也就是
\(dp[i][j]=dp[i-1][j+1]*j+dp[i-1][j-1]+(j-(i>st)-(i>ed))\)
初值 \(dp[1][1]=1\),答案為 \(dp[n][1]\)
程式碼
#include <cstdio> using namespace std; const int N=2011,mod=1000000007; int n,st,ed,dp[N][N]; int mo(int x,int y){return x+y>=mod?x+y-mod:x+y;} int main(){ scanf("%d%d%d",&n,&st,&ed); dp[1][1]=1; for (int i=2;i<=n;++i) if (i==st||i==ed){ for (int j=1;j<=i;++j) dp[i][j]=mo(dp[i-1][j-1],dp[i-1][j]); }else{ int now=(i>st)+(i>ed); for (int j=1;j<=i;++j) dp[i][j]=mo(1ll*dp[i-1][j-1]*(j-now)%mod,1ll*dp[i-1][j+1]*j%mod); } return !printf("%d",dp[n][1]); }