[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]i≠s,i≠t,c=(i>s)+(i>t)
dp[i][j]=dp[i−1][j−1]+dp[i−1][j]i=sori=t
原因解释:
1.i≠s,i≠t时,可将i单独成段,插在任意位置(若s以过则不能插首,若t已过则不能插尾),则贡献为(j−c)∗dp[i−1][j−1]。
或将i用于两段的合并,插在任意夹缝中,易知i比两段大,故操作合法,贡献为j∗dp[i−1][j+1]。
2.i=sori=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;
}
深深地感到自己的弱小。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步