火題大戰Vol.0 B 計數DP

liuchanglc發表於2020-08-19

火題大戰Vol.0 B

題目描述

\(n\) 個沙茶,被編號 \(1\)~$ n$。排完隊之後,每個沙茶希望,自己的相鄰的兩人只要無一個人的編號和自己的編號相差為 \(1\)\(+1\)\(-1\))就行;

現在想知道,存在多少方案滿足沙茶們如此不苛刻的條件。

輸入格式

只有一行且為用空格隔開的一個正整數 \(N\)

輸出格式

一個非負整數,表示方案數對 \(7777777\) 取模。

樣例

樣例輸入

4

樣例輸出

2

樣例解釋

有兩種方案 \(2\ 4\ 1\ 3\)\(3\ 1\ 4\ 2\)

資料範圍與提示

對於\(30\%\)的資料滿足\(N \leq 20\)

對於\(100\%\)的資料滿足\(1 \leq N \leq 1000\) ;

分析

我們設 \(f[i][j][0]\) 為填了 \(1\)\(i\),當前有 \(j\) 對兩兩之間相差一的人,並且\(i\)\(i-1\)不相鄰的方案數

\(f[i][j][1]\) 為填了 \(1\)\(i\),當前有 \(j\) 對兩兩之間相差一的人,並且\(i\)\(i-1\)相鄰的方案數

對於\(f[i][j][0]\),如果我們在這\(j\)對人的中間插入一個數,那麼兩兩之間相差一的人會少一對,因為此時\(i\)\(i-1\)不相鄰

轉移方程 \(f[i+1][j-1][0]+=j \times f[i][j][0]\)

如果我們在\(i\)的旁邊插入\(i+1\),那麼兩兩之間相差一的人會多一對,並且\(i\)\(i+1\)相鄰,因此會轉移至 \(f[i+1][j+1][1]\)

轉移方程 \(f[i+1][j+1][1]+=2 \times f[i][j][0]\)

此時,我們在剩下的位置插入不會對對數產生影響,即

\(f[i+1][j][0]+=(i-1-j) \times f[i][j][0]\)

對於\(f[i][j][1]\) 如果我們在\(i\)\(i-1\)的中間插入\(i+1\),則有

\(f[i+1][j][1]+=f[i][j][1]\)

如果我們在\(i\)的另一邊插入\(i+1\),則有

\(f[i+1][j+1][1]+=f[i][j][1];\)

如果我們在其它的 \(j-1\) 個空位中插入,則有

\(f[i+1][j-1][0]+=f[i][j][1]*(j-1)\)

如果我們在其它的空位中插入,則有

\(f[i+1][j][0]+=f[i][j][1]*(i-j)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
#define int long long
int f[maxn][maxn][3];
const int mod=7777777;
signed main(){
	int n;
	scanf("%lld",&n);
	f[2][1][1]=2;
	for(int i=1;i<=n;i++){
		for(int j=0;j<i;j++){
			f[i+1][j-1][0]+=j*f[i][j][0];
			f[i+1][j-1][0]%=mod;
			f[i+1][j+1][1]+=2*f[i][j][0];
			f[i+1][j+1][1]%=mod;
			if(i-j-1>0){
				f[i+1][j][0]+=(i-1-j)*f[i][j][0];
				f[i+1][j][0]%=mod;
			}
			if(j-1>0) {
				f[i+1][j-1][0]+=f[i][j][1]*(j-1);
				f[i+1][j-1][0]%=mod;
			}
			f[i+1][j][1]+=f[i][j][1];
			f[i+1][j][1]%=mod;
			f[i+1][j+1][1]+=f[i][j][1];
			f[i+1][j+1][1]%=mod;
			f[i+1][j][0]+=f[i][j][1]*(i-j);
			f[i+1][j][0]%=mod;
		}
	}
	printf("%lld\n",f[n][0][0]);
	return 0;
}

相關文章