• Atcoder Grand Contest 002 F Leftmost Ball(dp)


    Atcoder 题面传送门 & 洛谷题面传送门

    这道 Cu 的 AGC F 竟然被我自己想出来了!!!(((

    首先考虑什么样的序列会被统计入答案。稍微手玩几组数据即可发现,一个颜色序列 \(c_1,c_2,\cdots,c_{nk}\) 满足条件当前仅当对于从左往右数第 \(i\)\(0\) 号颜色的位置 \(p_i\)\([1,p_i-1]\) 中非零颜色的种类 \(<i\)。简单证明一下,必要性:如果 \(\exist i\in[1,n]\) 满足 \([1,p_i-1]\) 中颜色种类 \(\ge i\),那么 \([1,p_i-1]\) 中至少出现了 \(i\) 种颜色,也就至少应当有 \(i\)\(0\) 号颜色的球。但事实上 \([1,p_i-1]\) 中只有 \(i-1\)\(0\) 号颜色的球,矛盾。充分性:我们假设第 \(i\) 种颜色第二次出现的位置(也就是第一次出现的位置被替换为 \(0\) 后,剩余部分第一次出现的位置)为 \(q_i\),我们将 \(q_i\) 从小到大排个序,由假设第 \(i\) 小的 \(q_i\) 必定 \(\le p_i\),因此我们只需将第 \(i\) 个黑球替换为第 \(i\) 小的 \(q_i\) 对应的颜色即可还原出原序列。

    接下来考虑发现这个性质之后怎样计算答案,我们考虑一个填补空隙的思想,设 \(dp_{i,j}\) 表示填好了 \(i\) 个黑球的位置以及前 \(j\) 种颜色的剩余 \(k-1\) 个球的位置的方案数,考虑转移,由以下两种情况:

    • 我们新放了一个黑球,为了避免重复,我们强制要求该黑球必须放在从左往右数的第一个空隙处,方案唯一,即 \(dp_{i+1,j}\leftarrow dp_{i,j}\)
    • 我们新添了一种颜色,那么我们需先从剩余 \(n-j\) 种颜色中选择一种颜色,再将这种颜色的剩余 \(k-1\) 个球任意放入剩余 \(nk-j(k-1)-i\) 个空隙,方案数为 \(\dbinom{nk-j(k-1)-i}{k-1}\times(n-j)\)。可真的是这样吗?不难发现这样会重复计算,举个例子,\(n=2,k=2\),现在已经填好了两个 \(0\) 的位置,即 \(0\ 0\ -\ -\)(其中 \(-\) 表示空隙),那么我们在 \(dp\) 转移的过程中会出现先选择一个 \(1\),填入 \(3\) 位置,再选择一个 \(2\),填入 \(4\) 位置,即 \(0\ 0\ -\ -\to 0\ 0\ 1\ -\to 0\ 0\ 1\ 2\),也有可能出现先选择一个 \(2\),填入 \(4\) 位置,再选择一个 \(1\),填入 \(3\) 位置,\(0\ 0\ -\ -\to 0\ 0\ -\ 2\to 0\ 0\ 1\ 2\),不难发现这两种情况的结局是一样的,都是 \(0\ 0\ 1\ 2\),不过却因为钦定填颜色的顺序不同而被我们算了两次。怎样解决这个问题呢?这里有一个避免重复的技巧,我们还是将 \(k-1\) 个球塞入 \(nk-j(k-1)-i\) 个空隙,不过我们强制要求有一个球必须放入从左往右数的第一个空隙中,方案数就变为了 \(\dbinom{nk-j(k-1)-i-1}{k-2}\times(n-j)\),不难发现在这种情况下,我们塞球的顺序永远是先塞第一次出现位置最靠前的颜色,也就不会出现上面的情况了。\(dp\) 转移方程式为 \(dp_{i,j+1}\leftarrow dp_{i,j}\times\dbinom{nk-j(k-1)-i-1}{k-2}\times(n-j)\)

    到这里你可能还有个小问题,就是上面“从左往右数第 \(i\)\(0\) 号颜色的位置 \(p_i\)\([1,p_i-1]\) 中非零颜色的种类 \(<i\)”怎样在 DP 方程中体现出来。其实很简单,我们在求 DP 值的时候只保留 \(j\le i\)\(dp_{i,j}\) 即可,显然这样转移总是合法的。

    时间复杂度 \(\mathcal O(nk)\)

    最后,文件名打错,爆零两行泪,这题是某场模拟赛的题,现场想到正解了,却因为文件名打错而爆零了/kk

    const int MAXN=2e3;
    const int MOD=1e9+7;
    int n,k,dp[MAXN+5][MAXN+5];
    int fac[MAXN*MAXN+5],ifac[MAXN*MAXN+5];
    void init_fac(int n){
    	fac[0]=ifac[0]=ifac[1]=1;
    	for(int i=2;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i]*ifac[i-1]%MOD;
    }
    int binom(int x,int y){return 1ll*fac[x]*ifac[x-y]%MOD*ifac[y]%MOD;}
    int main(){
    //	freopen("colorful.in","r",stdin);freopen("colorful.out","w",stdout);
    	scanf("%d%d",&n,&k);if(!(k^1)) return printf("1\n"),0;
    	dp[0][0]=1;init_fac(n*k);
    	for(int i=1;i<=n;i++) for(int j=0;j<=i;j++){
    		dp[i][j]=(dp[i-1][j]+1ll*dp[i][j-1]*binom(n*k-(j-1)*(k-1)-i-1,k-2)%MOD*(n-j+1)%MOD)%MOD;
    	} printf("%d\n",dp[n][n]);
    	return 0;
    }
    
  • 相关阅读:
    设计模式:Prototype 原型模式
    [C++STDlib基础]关于单字符的操作——C++标准库头文件<cctype>
    Android开发之简单的电子相册实现
    autotools入门笔记(二)——创建和使用静态库、动态库
    Dreamer 框架 比Struts2 更加灵活
    Redis集群明细文档
    【Servlet3.0新特性】第03节_文件上传
    POJ 3264 Balanced Lineup
    利用jquery对ajax操作,详解原理(附代码)
    C语言实现修改文本文件中的特定行
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc002F.html
Copyright © 2020-2023  润新知