AGC02F Leftmost Ball

Counting 苦手本来都准备白兰了,但祁神发现了关键的性质然后就发现可做了

稍作观察我们就可以发现对于一个最终合法的序列,其任意一个前缀中白球的数量都必须大于等于这段前缀的颜色数

直接对长度为 \(n\times k\) 的序列 DP 复杂度显然不能接受,不过我们发现我们只关心每种颜色出现的第一个球的位置,对于剩下的 \(k-2\) 个球其实往后随便怎么放都不影响序列的合法性

设状态 \(f_{i,j}\) 表示已经放了 \(i\) 个白球,之前共出现了 \(j\) 种颜色,且这 \(j\) 种颜色的球已经全部放完的方案数,任何时刻都需要保证 \(i\ge j\)

转移分两类,一种是放一个白球,这种情况就是 \(f_{i+1,j}\leftarrow f_{i,j}\),否则考虑放一个之前没出现的颜色的球

首先选颜色有 \(n-j\) 种方案,这种颜色剩下的 \(k-2\) 个球可以放在后面剩下的 \(n\times k-i-1-j\times (k-1)\) 个位置,用组合数算一下即可

总复杂度 \(O(nk)\),注意特判 \(k=1\) 的情形

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=2005,mod=1e9+7;
int n,k,f[N][N],fact[N*N],ifac[N*N];
inline void inc(int& x,CI y)
{
    if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
    for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
    RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
    for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
    if (n<0||m<0||n<m) return 0;
    return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main()
{
    RI i,j; scanf("%d%d",&n,&k);
    if (k==1) return puts("1"),0;
    init(n*k); f[0][0]=1;
    for (i=0;i<=n;++i) for (j=0;j<=i;++j)
    {
        if (!f[i][j]) continue;
        //printf("f[%d][%d] = %d\n",i,j,f[i][j]);
        inc(f[i+1][j],f[i][j]);
        inc(f[i][j+1],1LL*f[i][j]*(n-j)%mod*C(n*k-i-1-j*(k-1),k-2)%mod);
    }
    return printf("%d",f[n][n]),0;
}
posted @ 2024-07-24 20:04  空気力学の詩  阅读(16)  评论(0编辑  收藏  举报