[CEOI2016]kangaroo

前言:\(OI\)中有一些奇思妙想,可能第一次自己想不出来,不要懊恼,不要沮丧,认真地去学习它,下次遇到就不怕了_

正文:考虑\(dp\),设状态量\(dp[i][j]\)表示前\(i\)个数分\(j\)段(任意一段满足条件)的方法数。

先给出状态转移方程:

\[dp[i][j]=(j-c)*dp[i-1][j-1]+j*dp[i-1][j+1]\quad i\ne s,i\ne t,c=(i>s)+(i>t) \]

\[dp[i][j]=dp[i-1][j-1]+dp[i-1][j]\quad i=s\quad or\quad i=t \]

原因解释:

1.\(i\ne s,i\ne t\)时,可将\(i\)单独成段,插在任意位置(若\(s\)以过则不能插首,若\(t\)已过则不能插尾),则贡献为\((j-c)*dp[i-1][j-1]\)

或将\(i\)用于两段的合并,插在任意夹缝中,易知\(i\)比两段大,故操作合法,贡献为\(j*dp[i-1][j+1]\)

2.\(i=s\quad or\quad i=t\)时,\(i\)只能放在首或尾,可与相邻段落合并,可单独成段,故贡献为\(dp[i-1][j-1]+dp[i-1][j]\)

代码如下,仅供参考:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e3+10;
const int mod=1e9+7;
int n,s,t;
ll dp[maxn][maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int main(){
	n=read();s=read(),t=read();
	dp[1][1]=1;
	for(int i=2;i<=n;i++)
		if(i!=s&&i!=t)
			for(int j=1;j<=i;j++)
				dp[i][j]=(dp[i-1][j-1]*(j-(i>s)-(i>t))+dp[i-1][j+1]*j)%mod;
		else for(int j=1;j<=i;j++)
				dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;
	printf("%lld\n",dp[n][1]);
	return 0;
}

深深地感到自己的弱小。

posted @ 2020-10-18 21:01  syzf2222  阅读(135)  评论(0编辑  收藏  举报