AGC002 F - Leftmost Ball

版权声明:...............转载说一声并注明出处qaq............... https://blog.csdn.net/L_0_Forever_LF/article/details/79980690

问题相当于计算有n个颜色0的球,其他n-1种颜色各有m-1个球,一个合法的序列是任意一个前缀中颜色0的球的数量>其他颜色的颜色数量

为了方便计数,我们不妨把每个颜色为0的球和一个颜色捆绑,即对于序列中(除了颜色0)出现的第i种颜色,我们将它捆绑到第i个颜色为0的球身上

然后可以设出一个dp,f[i][j][k]代表放了i个球,已经放了j个颜色为0的球,其他颜色已经出现了k种的方案数
状态数O(n3k),转移O(1)

然后可以发现第一维i是没有必要的qaq
可以直接dp,f[i][j]表示放了i个颜色为0的球,其他颜色已经出现了k种的方案数
两种转移,放一个颜色为0的球和放一种颜色的所有球,组合数什么的算一下
状态数O(n2),转移O(1)

最后答案乘上n!代表给颜色0的不同捆绑方案

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

const int maxn = 2000*2000+5;
const int mod  = 1e9+7;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}

int pw(int x,int k)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
        re=(ll)re*x%mod;
    return re;
}
int inv(int x){ return pw(x,mod-2); }

int s[maxn],invs[maxn];
void pre()
{
    s[0]=1; for(int i=1;i<maxn;i++) s[i]=(ll)s[i-1]*i%mod;
    invs[maxn-1]=inv(s[maxn-1]);
    for(int i=maxn-2;i>=0;i--) invs[i]=(ll)invs[i+1]*(i+1)%mod;
}
int C(int n,int m){return (ll)s[n]*invs[m]%mod*invs[n-m]%mod;}

int n,m;
int f[2005];

int main()
{
    pre();

    scanf("%d%d",&n,&m); int u=n*m;
    if(m==1) return puts("1"),0;

    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<i;j++)
            add(f[j+1],(ll)f[j]*C(u-i-j*(m-1)-1,m-2)%mod);
    }
    int ans=f[n];
    ans=(ll)ans*s[n]%mod;
    printf("%d\n",ans);

    return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页