1. 程式人生 > 其它 >#線性dp#洛谷 5999 [CEOI2016]kangaroo

#線性dp#洛谷 5999 [CEOI2016]kangaroo

線性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]);
}