1. 程式人生 > >【DP】2018國慶三校聯考

【DP】2018國慶三校聯考

題意:

在N個格子之間,放入D-1個隔板(可以重合),要求每兩個相鄰隔板之間距離不超過M。求方案數。 N,M2000N,M\leq 2000 D1012D\leq 10^{12}

分析:

儘管D的範圍非常大,但其實很多隔板之間的距離都為0,所以可以考慮距離不為0的隔板的放置方案(即不能重合的方案)。再利用組合數求出在所有隔板中選擇一定數量的方案數。

所以可以設DP(i,j)DP(i,j)表示用i個格子,放置j各隔板,每兩個之間距離不超過M的方案數。 這可以利用滑窗優化在O(n2)O(n^2)範圍內算出來。

然後組合數可以直接暴力求,複雜度也是O(n2)O(n^2)

的。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 2010
#define MOD 998244353
using namespace std;
typedef long long ll;
ll dp[MAXN][MAXN];
ll fac[MAXN],ifac[MAXN];
int t;
ll n,m,d;
ll fsp(ll x,ll y){
	ll res=
1; while(y){ if(y&1ll) res=res*x%MOD; x=x*x%MOD; y>>=1ll; } return res; } ll C(ll x,ll y){ ll sum=1; for(ll i=x;i>x-y;i--){ ll i1=i%MOD; sum=sum*i1%MOD; } sum=sum*ifac[y]%MOD; return sum; } int main(){ fac[0]=1; for(ll i=1;i<=2000;i++) fac[i]=fac[i-1]*i%MOD; ifac[
2000]=fsp(fac[2000],MOD-2); for(ll i=2000;i>=1;i--) ifac[i-1]=ifac[i]*i%MOD; while(SF("%lld%lld%lld",&n,&d,&m)!=EOF){ if(n==0&&d==0&&m==0) break; m--; if(m==0){ PF("%d\n",(n==0)); continue; } memset(dp,0,sizeof dp); dp[0][0]=1; for(int i=1;i<=n;i++){ ll sum=dp[i-1][0]; for(int j=1;j<=n;j++){ dp[i][j]=sum; sum=(sum+dp[i-1][j])%MOD; if(j-m>=0) sum=(sum-dp[i-1][j-m]+MOD)%MOD; } } ll ans=0; for(int i=1;i<=n;i++) ans=(ans+C(d,i)*dp[i][n]%MOD)%MOD; PF("%lld\n",ans); } }