【AGC002F】Leftmost Ball DP 数学

来源:互联网 发布:淘宝崔天琪同款手环 编辑:程序博客网 时间:2024/06/08 11:34

题目大意

  有n种颜色的球,每种m个。现在zjt把这nm个球排成一排,然后把每种颜色的最左边的球染成第n+1种颜色。求最终的颜色序列有多少种,对1000000007取模。

  n,m2000

题解

  我们称颜色为1~n的球为正常颜色的球,颜色为n+1的球为白球。

  我们先钦定每种颜色最左边那个球的出现顺序为1~n,从左往右的第i个白球的球对应着第i个颜色。

  考虑从后往前放,设当前序列最前面有x个白球的球,那么当前放的白球要放在最前面,其他m1个正常颜色的球中最左边那个要放在当前最左边正常颜色的球的左边。剩下m2个可以随便放。

  设fi,j为放了后i种颜色的球,序列最前面有j个白球。

fi,j=kj1fi1,k×(imj1m2)

  可以发现后面那个组合数和k无关,所以可以用后缀和优化

  时间复杂度:O(nm)

代码

#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#include<ctime>#include<utility>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> pii;ll p=1000000007;ll fac[4000010];ll ifac[4000010];ll inv[4000010];void init(int n){    int i;    fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;    for(i=2;i<=n;i++)    {        inv[i]=-(p/i)*inv[p%i]%p;        fac[i]=fac[i-1]*i%p;        ifac[i]=ifac[i-1]*inv[i]%p;    }}ll c(int x,int y){    if(x<y)        return 0;    return fac[x]*ifac[y]%p*ifac[x-y]%p;}ll f[2010][2010];int main(){    int n,k;    scanf("%d%d",&n,&k);    if(k<=1)    {        printf("1\n");        return 0;    }    init(n*k);    int i,j;    f[0][0]=1;    for(i=1;i<=n;i++)        for(j=n;j>=0;j--)        {            if(j<=i)                f[i][j]+=f[i-1][j-1]*c(k*i-j-1,k-2)%p;            f[i][j]=(f[i][j]+f[i][j+1])%p;        }    ll ans=(f[n][0]%p+p)%p;    ans=ans*fac[n]%p;    printf("%lld\n",ans);    return 0;}
原创粉丝点击