• 【AtCoder】AGC022 F


    【题目】F - Leftmost Ball

    【题意】给定n种颜色的球各k个,每次以任意顺序排列所有球并将每种颜色最左端的球染成颜色0,求有多少种不同的颜色排列。n,k<=2000。

    【算法】计数DP

    【题解】只看黑体字部分即可

    自己考虑了几种计数方案,都不能实现。一种从左到右,但要记录每种球剩余多少,不可行。一种从右到左枚举白球包含区间填充,但因为只看白球,每种颜色没有关键球,会有重复统计的问题。

    计数的关键在于白球的原色不重要以及每种颜色关注最左端的球(这里不含变成白球的球)。

    本题既然nk能分开,就没必要整个序列推进,只抽象地枚举白球数和颜色数就可以了,这是因为本题的条件很优美,不需要考虑位置的因素。

    设$f[i][j]$表示前i个白球,仅包含j种颜色的最大前缀的排列数。这里只要保证$j leq i$就可以满足颜色数不多于白球数。

    转移可以增加一个白球,或增加一种颜色。增加颜色时固定最左端球的位置,然后在前面球剩余的位置任意填充。(这里既不考虑填充前面剩余位置的问题,因为紧凑。也不考虑前面每种球的剩余数量,因为每种填充方案的剩余位置数都是确定的。计数问题常常考虑每一种前面的方案共有的性质然后抽象出来转移,即对于前面每一个方案如何如何后就变成现在的方案)

    $$f[i][j]=f[i-1][j]+inom{n-i+(n-j+1)*(k-1)-1}{k-2}*(n-j+1)*f[i][j-1]$$

    后面的(n-j+1)可以改为最后乘上n!。

    复杂度O(n^2)。

    代码来自:zhanglexing

    #include<cstdio>
    #define ll long long
    const int N=2005,M=4e6+5,mo=1e9+7;
    int n,m,nm,i,j,f[N];
    int fact[M],inv[M],fv[M];
    void init(int n){
        fact[0]=fact[1]=inv[1]=fv[0]=fv[1]=1;
        for (i=2;i<=n;i++){
            fact[i]=(ll)fact[i-1]*i%mo;
            inv[i]=(ll)inv[mo%i]*(mo-mo/i)%mo;
            fv[i]=(ll)fv[i-1]*inv[i]%mo;
        }
    }
    int C(int n,int m){return (ll)fact[n]*fv[m]%mo*fv[n-m]%mo;}
    int main(){
        scanf("%d%d",&n,&m);m--;
        if (!m){puts("1");return 0;}
        nm=n*m+n;init(nm);f[0]=1;
        for (i=1;i<=n;i++) for (j=1;j<=i;j++)
            f[j]=(f[j]+(ll)(n-j+1)*
                C(nm-i-(j-1)*m-1,m-1)%mo*f[j-1])%mo;
        printf("%d",f[n]);
    }
    View Code
  • 相关阅读:
    如何最大限度提高.NET的性能
    Webserivce简单安全验证
    一些NLP相关的JD,作参考
    拼多多的故事
    storm的一些相关文章
    这篇文章不错,仔细读读,码农晋升为技术管理者后,痛并快乐着的纠结内心
    protobuf的反射机制
    如何清理Docker占用的磁盘空间?
    经典面试题:浏览器是怎样解析CSS的?
    代码编辑器横评:为什么 VS Code 能拔得头筹
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8865992.html
Copyright © 2020-2023  润新知