[DP 组合数学] BZOJ 4498 魔法的碰撞


传送门:http://blog.csdn.net/visit_world/article/details/51090964

dp其中一维解释一下,表示可以放且必须放的一个位置


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define P 1000000007
using namespace std;
typedef long long ll;

inline char nc(){
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x){
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

int maxn=1000000;
ll fac[1000005],inv[1000005];

inline void Pre()
{
	fac[0]=1;
	for (int i=1;i<=maxn;i++)
		(fac[i]=fac[i-1]*i)%=P;
	inv[1]=1;
	for (int i=2;i<=maxn;i++)
		(inv[i]=(P-P/i)*inv[P%i])%=P;
	inv[0]=1;
	for (int i=1;i<=maxn;i++)
		(inv[i]*=inv[i-1])%=P;
}

inline ll C(int n,int m){
	return ((fac[n]*inv[m])%P*inv[n-m])%P;
}

ll f[2][3205][41];
int d[45],m,n,sum;

int main()
{
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	Pre();
	read(m), read(n);
    for (int i = 1; i <= n; ++i)
        read(d[i]),--d[i],sum+=d[i]<<1;
    sort(d+1,d+n+1);
	int t=0;
    f[t][0][1]=1;
    for (int i=n;i;i--,t^=1)
    {
        memset(f[t^1],0,sizeof(f[t^1]));
        int v=d[i];
        for (int j=0;j<=sum;j++) 
			for (int k=1;k<=n;k++) 
				if (f[t][j][k])
        		{
           	 		ll x=f[t][j][k];
            		(f[t^1][j+v*2][k+1]+=x*k%P)%=P;
            		(f[t^1][j+v][k]+=x*k*2%P)%=P;
            		(f[t^1][j][k-1]+=x*k%P)%=P;
        		}
    }
    ll ret=0;
    for (int j=min(sum,m-n);~j;j--)
		if (f[t][j][0])
        	(ret+=f[t][j][0]*C(m-j,n)%P)%=P;
    printf("%lld\n",ret);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值