bzoj 4498: 魔法的碰撞(DP+组合数)

4498: 魔法的碰撞

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 145   Solved: 88
[ Submit][ Status][ Discuss]

Description

魔法总是令战斗的局面变幻莫测。
然而魔力的碰撞则更是天马行空,甚至会出现无法控制而自取灭亡的情况。
因此,魔力碰撞总是没有办法的办法。
不过在战场上大家可不会想太多了:看到敌人,直接一阵法术秒杀之,规则神马的都是浮云了。因此,必须布阵时就避免可能的魔力碰撞。
设想有一条长度为L的战线,你可以把你的魔法师们安排在战线上的每个格子。每一个魔法师都有一个攻击范围di,排兵时必须保证任意两个魔法师的攻击范围的较大值小于等于它们之间的距离(距离即为它们坐标的差值)。为了更好地迷惑敌人,你须要求出总共有多少种布阵的方案。

Input

第一行两个整数L,n,n代表魔法师个数。
第二行n个数,描述魔法师的攻击范围di。
N≤40,di≤40,L≤1000000

Output

一行,一个整数,代表方案数mod 1000000007的值。

Sample Input

9 3
1 2 4

Sample Output

42


如果当前所有FS的顺序已经确定了,并且它们是紧挨着站的,总共长度为w

那么答案就是C(len-w, n)

可是顺序没有确定,并且不同的顺序w也不一样,不过w很小可以DP

先将所有FS按攻击范围从大小排列(后面解释为什么要排序)

dp[i][j][k]表示已经放了前i个法师,当前权值为j,有k个空位的方案数

一个空位可以放若干个FS

DP时只有三种情况

①当前FS填上一个空位(空位-1),此时对w贡献为0,因是从大到小排序,所以插入后左右两边FS肯定是不用动的

②当前FS放在其中一个空位里(空位不变),此时对w贡献为该FS的攻击范围

③当前FS放在其中一个空位里(空位+1),此时对w贡献为该FS的攻击范围*2


#include<stdio.h>
#include<algorithm>
using namespace std;
#define mod 1000000007
#define LL long long
LL a[44], dp[44][44][3222], jc[1000005] = {1};
LL Pow(LL a, LL b)
{
	LL ans;
	ans = 1;
	while(b)
	{
		if(b%2==1)
			ans = (ans*a)%mod;
		a = (a*a)%mod;
		b /= 2;
	}
	return ans;
}
LL C(LL n, LL m)    
{
	LL ans;
	if(n<m)
		return 0;
	ans = (jc[n]*Pow((jc[m]*jc[n-m])%mod, mod-2)%mod)%mod;
	return ans;
}
int main(void)
{
	LL len, n, i, j, k, sum, ans;
	for(i=1;i<=1000000;i++)
		jc[i] = jc[i-1]*i%mod;
	scanf("%lld%lld", &len, &n);
	for(i=1;i<=n;i++)
		scanf("%lld", &a[i]);
	sort(a+1, a+n+1);
	for(i=1;i<=n/2;i++)
		swap(a[i], a[n-i+1]);
	for(i=1;i<=n;i++)
		a[i]--;
	dp[0][1][0] = 1;
	sum = 0;
	for(i=1;i<=n;i++)
	{
		sum += a[i];
		for(j=0;j<=i+1;j++)
		{
			for(k=0;k<=sum*2;k++)
			{
				dp[i][j][k] = dp[i-1][j+1][k]*(j+1)%mod;
				if(j>=1 && k>=a[i])
					dp[i][j][k] = (dp[i][j][k]+dp[i-1][j][k-a[i]]*j*2)%mod;
				if(j>=2 && k>=2*a[i])
					dp[i][j][k] = (dp[i][j][k]+dp[i-1][j-1][k-2*a[i]]*(j-1))%mod;
			}
		}
	}
	ans = 0;
	for(i=0;i<=2*sum;i++)
		ans = (ans+dp[n][0][i]*C(len-i, n))%mod;
	printf("%lld\n", ans);
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值