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

题意

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

题解

发现一个性质:对任何前缀,0的个数必须 出现的其它颜色种类数
可定义 dp[i][j] d p [ i ] [ j ] 表示有i个0,j种颜色都已经放完的方案数
转移:
新放了一个0
dp[i][j]+=dp[i1][j] d p [ i ] [ j ] + = d p [ i − 1 ] [ j ]
新放了一种颜色,这个颜色的第一个球必须放在最开始(表示这时候已经出现过这种颜色),(还有一个球变为了0)这种颜色还剩下 K2 K − 2 个球可以随便放,则需乘上组合数
dp[i][j]+=dp[i][j1]×CK2N×Ki(j1)(K1)1 d p [ i ] [ j ] + = d p [ i ] [ j − 1 ] × C N × K − i − ( j − 1 ) ( K − 1 ) − 1 K − 2
最后答案为 dp[N][N]×N! 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是 APTED+ 算法的伪代码及其解释: ``` function ComputeAPTED+(T1, T2): if T1 and T2 are both leaves: return abs(T1.label - T2.label) if T1 is a leaf and T2 is not: return T2.cost + ComputeAPTED+(T1, T2.leftmost_child) if T1 is not a leaf and T2 is a leaf: return T1.cost + ComputeAPTED+(T1.leftmost_child, T2) if T1.label != T2.label: return T1.cost + T2.cost else: M = ComputeMatching(T1.children, T2.children) return T1.cost + T2.cost + min_{m∈M}{ComputeAPTED+(T1.child(m.1), T2.child(m.2))} ``` 其中,`T1` 和 `T2` 分别表示两棵树,`label` 表示节点的标签,`cost` 表示将该节点从一个树中剪掉所需的代价,`leftmost_child` 表示左侧子节点,`children` 表示所有子节点的集合,`abs` 表示绝对值。 算法的主要思路是使用动态规划来计算两棵树之间的编辑距离,即将一棵树转换为另一棵树所需要的最小操作数。其中,第一行的 if 语句判断两个节点是否均为叶子节点,如果是则返回它们的标签之差。第二行和第三行的 if 语句分别判断其中一个节点是叶子节点,然后将该节点加入到另一棵树中去,同时递归计算子树的编辑距离。第四行的 if 语句比较两个节点的标签是否相同,如果不相同则直接返回它们的代价之和。如果标签相同,则计算所有子节点的匹配情况,并递归计算每个匹配情况下的最小编辑距离。 这里涉及到一个名为 `ComputeMatching` 的子函数,用于计算两个节点集合之间的最佳匹配。具体实现细节可以参考原论文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值