bzoj 4498 魔法的碰撞

4498: 魔法的碰撞

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 99 Solved: 64
[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
HINT

Source

[Submit][Status][Discuss]

HOME Back


【分析】

奇葩脑洞题,不看题解不会系列。
正常人一眼看过去->dp[i][j]表示前i个魔法师放在前j个位置的方案数。
于是正常人发现dp[i][j]没法从dp[i][j-1]转移过来…
那好,正常人脑洞出了一个奇葩做法。

我编不下去了看链接吧 YHX Orz
http://blog.csdn.net/sdfzyhx/article/details/72773890


【代码】

//bzoj 4498 魔法的碰撞
#include<bits/stdc++.h>
#define N 1000000
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int p=1000000007;
const int mxn=1000005;
int n,l;
ll ans,fac[mxn],inv[mxn];
int d[mxn],sum[mxn];
int dp[45][45][3205];
inline bool comp(int x,int y)
{
    return x>y;
}
inline void init()
{
    int i,j;
    fac[0]=inv[0]=inv[1]=1; 
    fo(i,1,N) fac[i]=fac[i-1]*i%p;
    fo(i,2,N) inv[i]=(ll)(p-p/i)*inv[p%i]%p;
    fo(i,1,N) inv[i]=inv[i]*inv[i-1]%p;
}
inline ll C(int n,int m)
{
    if(n<m) return 0;
    return fac[n]*inv[m]%p*inv[n-m]%p;
}
int main()
{
    init();
    int i,j,k;
    scanf("%d%d",&l,&n);
    fo(i,1,n) scanf("%d",&d[i]);
    fo(i,1,n) d[i]--;
    sort(d+1,d+n+1,comp);
    dp[0][1][0]=1;
    fo(i,1,n)
    {
        sum[i]=sum[i-1]+d[i];
        fo(j,0,i+1)
          fo(k,0,2*sum[i])
          {
              dp[i][j][k]=(dp[i][j][k]+(ll)dp[i-1][j+1][k]*(j+1)%p)%p;
              if(k>=d[i])
                dp[i][j][k]=(dp[i][j][k]+(ll)dp[i-1][j][k-d[i]]*2%p*j%p)%p;
              if(k>=2*d[i] && j>=1)
                dp[i][j][k]=(dp[i][j][k]+(ll)dp[i-1][j-1][k-2*d[i]]*(j-1)%p)%p;
          }
    }
    fo(i,0,2*sum[n])
      ans=(ans+(ll)dp[n][0][i]*C(l-i,n)%p)%p;
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值