bzoj4498: 魔法的碰撞

好题实吹 这种排列计数问题确实感觉无从下手啊

考虑暴力,是枚举每一个排列,排列的贡献为C(L-W,n),其中W是max(di-1,di)-1,即相邻两个魔法师之间不能放的位置

考虑到攻击范围比较小,一个人最多打80个位置,假如能够计算出对于每个W的排列方案数,就能算出答案了

排序消掉max,按大到小插入魔法师,那么对于当前魔法师,就有三种情况:

1、自己找个地方随便放,那么最后它对不能够放的位置的贡献就是2*(di-1)

2、放在其中一个魔法师旁边,贡献为di-1

3、放在两个魔法师之间,贡献为0

但是直接这样算方案的话,插入的魔法师会分成很多段,状态就很难表示了

考虑到我们并不关心魔法师具体的位置或者相对位置,只关心他打了多少位置

我们再弄一个关键,目前还剩下的空位

什么鬼?就是每次插入魔法师的时候,先想好未来让不让一个魔法师放到自己的左/右边,因为不关心位置所以位置可以直接+0,+1,或+2,当然插入进来也会用1个位置

这样就可以DP了,设f[i][j][k]表示前i个人,有多少位置可以插入,当前能打多少位置

注意假如+1,方案数要乘2,因为可以放左也可以放右

我预处理阶乘的逆元比较蠢,每个都求了一次,实际上把最后一个算出来不断往前乘就好了

 

upd:这里给出另一种做法,或许更好理解些

前面的都一样,到分成很多段那儿,我们可以设分成了多少段

设f[i][j][k][p]表示前i个人,分成了j段,能打了k个位置,p=0/1/2意思是左界和右界共到了多少个

思路是把两段合并,还是接在一段一边,还是插在某两段中间不相连

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=40+10;
const int maxd=40+10;
const int maxL=1e6+_;
const LL mod=1e9+7;

LL quick_pow(LL A,LL p)
{
    LL ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=ret*A%mod;
        A=A*A%mod;p/=2;
    }
    return ret;
}
LL fac[maxL],fac_inv[maxL];
void yu(int L)
{
    fac[0]=1,fac_inv[0]=1;
    for(int i=1;i<=L;i++)
        fac[i]=fac[i-1]*i%mod,fac_inv[i]=quick_pow(fac[i],mod-2);
}
LL C(int n,int m){return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;}

int d[maxn];
LL f[maxn][maxn][2*maxn*maxd];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int L,n,li=0;
    scanf("%d%d",&L,&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]),d[i]--,li+=2*d[i];
    sort(d+1,d+n+1);
    reverse(d+1,d+n+1);
    
    f[0][1][0]=1;
    for(int i=0;i<n;i++)
        for(int j=0;j<=n;j++)
            for(int k=0;k<=li;k++)
                if(f[i][j][k])//把第i+1个魔法师扔进来 
                {
                    f[i+1][j+1][k+2*d[i+1]]=(f[i+1][j+2][k+2*d[i+1]]+f[i][j][k]*j)%mod;
                    f[i+1][j][k+d[i+1]]=(f[i+1][j][k+d[i+1]]+f[i][j][k]*2*j)%mod;
                    if(j!=0)f[i+1][j-1][k]=(f[i+1][j-1][k]+f[i][j][k]*j)%mod;
                }
    yu(L);
    LL ans=0; int u=min(L-n,li);
    for(int i=1;i<=u;i++)
    {
        ans=(ans+C(L-i,n)*f[n][0][i])%mod;
    }
    printf("%lld\n",ans);
    
    return 0;
}

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=40+10;
const int maxd=40+10;
const int maxL=1e6+_;
const LL mod=1e9+7;

LL quick_pow(LL A,LL p)
{
    LL ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=ret*A%mod;
        A=A*A%mod;p/=2;
    }
    return ret;
}
LL fac[maxL],fac_inv[maxL];
void yu(int L)
{
    fac[0]=1,fac_inv[0]=1;
    for(int i=1;i<=L;i++)
        fac[i]=fac[i-1]*i%mod,fac_inv[i]=quick_pow(fac[i],mod-2);
}
LL C(int n,int m){return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;}

int d[maxn];
LL f[2][maxn][2*maxn*maxd][3];//枚举到第i个位置,有j段,覆盖长度为k,其中挨了多少边界
int main()
{
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    int L,n,li=0;
    scanf("%d%d",&L,&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]),d[i]--,li+=2*d[i];
    sort(d+1,d+n+1);
    reverse(d+1,d+n+1);
    
    int now=0;
    f[now][1][2*d[1]][0]=1;
    f[now][1][d[1]][1]=2;
    for(int i=1;i<n;i++)
    {
        memset(f[now^1],0,sizeof(f[now^1]));
        for(int j=1;j<=n;j++)
            for(int k=0;k<=li;k++)
                for(int p=0;p<=2;p++)
                    if(f[now][j][k][p])
                    {
                        f[now^1][j+1][k+2*d[i+1]][p]=(f[now^1][j+1][k+2*d[i+1]][p]+f[now][j][k][p]*(j-p+1))%mod;
                        f[now^1][j][k+d[i+1]][p]=(f[now^1][j][k+d[i+1]][p]+f[now][j][k][p]*(2*j-p))%mod;
                        if(j!=1)f[now^1][j-1][k][p]=(f[now^1][j-1][k][p]+f[now][j][k][p]*(j-1))%mod;
                        if(p!=2)
                        {
                            f[now^1][j+1][k+d[i+1]][p+1]=(f[now^1][j+1][k+d[i+1]][p+1]+f[now][j][k][p]*(2-p))%mod;
                            f[now^1][j][k][p+1]=(f[now^1][j][k][p+1]+f[now][j][k][p]*(2-p))%mod;
                        }
                    }
        now^=1;
    }
    
    yu(L);
    LL ans=0; int u=min(L-n,li);
    for(int i=1;i<=u;i++)
    {
        ans=(ans+C(L-i,n)*f[now][1][i][2])%mod;
    }
    printf("%lld\n",ans);
    
    return 0;
}

 

posted @ 2019-02-26 15:44  AKCqhzdy  阅读(272)  评论(0编辑  收藏  举报