【AtCoder2000】Leftmost Ball (DP+组合数)

版权声明:本文为博主原创文章,请随意转载(注明出处)。 https://blog.csdn.net/can919/article/details/82733596

题意

Snuke喜欢五颜六色的球。他总共有N×K个球,有N种颜色,每种颜色的球有K个。颜色编号为1到N。他将按照任意顺序排列所有球。然后,对于每种颜色,他将该颜色的最左边的球涂成颜色0,颜色0不同于N种原始颜色中的任何颜色。如将球排列为(1,2,1,2),染色后就变为 (0,0,1,2)。
所有操作后,球的颜色序列有多少种,求这个方案数mod 10^9+7。

题解

发现一个性质:对任何前缀,0的个数必须 出现的其它颜色种类数
可定义 d p [ i ] [ j ] 表示有i个0,j种颜色都已经放完的方案数
转移:
新放了一个0
d p [ i ] [ j ] + = d p [ i 1 ] [ j ]
新放了一种颜色,这个颜色的第一个球必须放在最开始(表示这时候已经出现过这种颜色),(还有一个球变为了0)这种颜色还剩下 K 2 个球可以随便放,则需乘上组合数
d p [ i ] [ j ] + = d p [ i ] [ j 1 ] × C N × K i ( j 1 ) ( K 1 ) 1 K 2
最后答案为 d p [ N ] [ N ] × N ! (dp时没有考虑颜色的区别,还需乘上颜色的排列数)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=2005,MOD=1000000007;

int PowMod(int a,int b)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=1LL*ret*a%MOD;
        a=1LL*a*a%MOD;
        b>>=1;
    }
    return ret;
}

int fac[MAXN*MAXN],ifac[MAXN*MAXN];

void Init()
{
    fac[0]=1;
    for(int i=1;i<MAXN*MAXN;i++)
        fac[i]=1LL*fac[i-1]*i%MOD;
    ifac[MAXN*MAXN-1]=PowMod(fac[MAXN*MAXN-1],MOD-2);
    for(int i=MAXN*MAXN-2;i>=0;i--)
        ifac[i]=1LL*ifac[i+1]*(i+1)%MOD;
}
int C(int n,int r)
{return 1LL*fac[n]*ifac[n-r]%MOD*ifac[r]%MOD;}

int N,K,dp[MAXN][MAXN];

int main()
{
    Init();
    scanf("%d%d",&N,&K);
    if(K==1)
    {
        puts("1");
        return 0;
    }
    dp[0][0]=1;
    for(int i=0;i<=N;i++)
        for(int j=0;j<=i;j++)
        {
            if(i)
                dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
            if(j)
                dp[i][j]=(dp[i][j]+1LL*dp[i][j-1]*C(N*K-i-(K-1)*(j-1)-1,K-2)%MOD)%MOD;
        }
    int ans=1LL*dp[N][N]*fac[N]%MOD;
    printf("%d\n",ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/can919/article/details/82733596