BZOJ4498: 魔法的碰撞【DP】

25 篇文章 0 订阅

4498: 魔法的碰撞

我们先考虑全部紧凑的情况,也就是没有多余的空格的情况(将 D i D_i Di–,先不考虑魔法师占的空间)。

这里用了一个很巧妙的方法,多加一维,表示预留的空位。

加入A会有三种情况(E表示空位):A,AE或EA,EAE。

对于第一种,表示A两边都有魔法师。

对于第二种,表示A的一边有,一边没有。

对于第三种,表示A的两边都是空的。

如果我们按照从大到小的顺序来,就会发现只需要考虑当前情况就可以了。

比如说是EAE的情况,我们就可以直接加上A的距离了。

此时可以设 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k]表示前i个魔法师,还有j个空位,用了k距离的方案数。

最后将那些多余的空格放入,用组合数(隔板法)算一下就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MOD=1000000007;
int m,n,Ans,Sum,fac[1000005],inv[1000005],a[45],f[2][45][10005];
void MO(int &x){if(x>=MOD) x-=MOD;}
int qsm(int x,int b){
	int Mul=1;
	for(;b;b>>=1,x=1ll*x*x%MOD) if(b&1) Mul=1ll*Mul*x%MOD;
	return Mul;
}
int C(int x,int y){if(x<y) return 0;return 1ll*fac[x]*inv[y]%MOD*inv[x-y]%MOD;}
bool cmp(int x,int y){return x>y;}
int main(){
	scanf("%d%d",&m,&n);fac[0]=1;
	for(int i=1;i<=m;i++) fac[i]=1ll*fac[i-1]*i%MOD;
	inv[m]=qsm(fac[m],MOD-2);
	for(int i=m-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%MOD;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),--a[i];
	sort(a+1,a+1+n,cmp);f[0][1][0]=1;
	for(int i=1,lst=0,now=1;i<=n;i++,lst^=1,now^=1){
		Sum+=a[i];
		for(int j=0;j<=i+1;j++)
		for(int k=0;k<=(Sum<<1);k++){
			f[now][j][k]=1ll*(j+1)*f[lst][j+1][k]%MOD;
			if(k>=a[i]) MO(f[now][j][k]+=2ll*j*f[lst][j][k-a[i]]%MOD);
			if(j&&k>=(a[i]<<1)) MO(f[now][j][k]+=1ll*(j-1)*f[lst][j-1][k-(a[i]<<1)]%MOD);
		}
	}
	for(int i=0;i<=min(Sum<<1,m-n);i++) MO(Ans+=1ll*f[n&1][0][i]*C(m-i,n)%MOD);
	printf("%d\n",Ans);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值