【DP】2018国庆三校联考D1T1

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34454069/article/details/82959283

题意:

在N个格子之间,放入D-1个隔板(可以重合),要求每两个相邻隔板之间距离不超过M。求方案数。
N , M 2000 N,M\leq 2000
D 1 0 12 D\leq 10^{12}


分析:

尽管D的范围非常大,但其实很多隔板之间的距离都为0,所以可以考虑距离不为0的隔板的放置方案(即不能重合的方案)。再利用组合数求出在所有隔板中选择一定数量的方案数。

所以可以设 D P ( i , j ) DP(i,j) 表示用i个格子,放置j各隔板,每两个之间距离不超过M的方案数。
这可以利用滑窗优化在 O ( n 2 ) O(n^2) 范围内算出来。

然后组合数可以直接暴力求,复杂度也是 O ( n 2 ) 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);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_34454069/article/details/82959283