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

来源:互联网 发布:查询域名注册信息 编辑:程序博客网 时间:2024/05/02 04:24

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 longLL 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;}


原创粉丝点击