[AGC002F] Leftmost Ball 题解

非常妙的计数 dp 题,有利于我们训练思维。

Description

传送门

Solution

首先,本题显然是一道计数 dp \text{dp} dp 题。在构造 dp \text{dp} dp 之前,我们不妨先考虑什么样的盘面 a a a 是合法的。

不难得到下面的判定算法:考虑从左到右扫描 a a a,维护前缀中 0 0 0 的数量及非 0 0 0 颜色的种数,那么 a a a 合法当且仅当前者始终不小于后者。

考虑将上述判定流程内化到 dp \text{dp} dp 状态中。令 f i , j f_{i,j} fi,j 表示,目前钦定了前 i i i 0 0 0,在钦定过程中也钦定了 j j j 种非 0 0 0 的颜色,此时的方案数。转移的时候,分成两类考虑:

  • 填上 0 0 0,则 f i , j → f i + 1 , j f_{i,j} \to f_{i+1,j} fi,jfi+1,j
  • 扩充一种新颜色。不难发现有 n − j n-j nj 种颜色备选,且选定了颜色过后,要在空余的 n k − i − j ( k − 1 ) − 1 nk-i-j(k-1)-1 nkij(k1)1 个位置中选择 k − 2 k-2 k2 个位置放上该颜色。则 ( n − j ) ( n k − i − j ( k − 1 ) − 1 k − 2 ) f i , j → f i , j + 1 (n-j) {nk-i-j(k-1)-1 \choose k-2} f_{i,j} \to f_{i,j+1} (nj)(k2nkij(k1)1)fi,jfi,j+1

边界: f 0 , 0 = 1 f_{0,0}=1 f0,0=1
答案: f n , n f_{n,n} fn,n

特判 k = 1 k=1 k=1 即可。时间复杂度 O ( n 2 ) O(n^2) O(n2)

Summary

想到了判定方法,但为什么我没有设计出 dp \text{dp} dp 呢?这不是很自然吗?可能是……躺床上枕头有点翘导致脖子那里血脉不是特别通畅导致的?

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2005,maxp=4000005,mod=1e9+7;

int n,k;
int jc[maxp],inv[maxp],f[maxn][maxn];

void chksum(int x,int &y){y=(y+x)%mod;}
int quick_power(int x,int y){
	int res=1;
	for (;y;y=y>>1,x=(x*x)%mod){
		if (y&1)  res=(res*x)%mod;
	}
	return res;
}
void init(int n){
	jc[0]=1;
	for (int i=1;i<=n;i++)  jc[i]=(jc[i-1]*i)%mod;
	
	inv[n]=quick_power(jc[n],mod-2);
	for (int i=n-1;i>=0;i--)  inv[i]=(inv[i+1]*(i+1))%mod;
}
int C(int x,int y){
	if (x<0||y<0||x<y)  return 0;
	return ((jc[x]*inv[y])%mod*inv[x-y])%mod;
}

signed main(){
	cin>>n>>k,init(maxp-5),f[0][0]=1;
	if (k==1)  return puts("1"),0;
	for (int i=0;i<=n;i++){
		for (int j=0;j<=i;j++){
			if (i<n)  chksum(f[i][j],f[i+1][j]);
			if (j<i)  chksum(f[i][j]*(n-j)%mod*C(n*k-i-j*(k-1)-1,k-2),f[i][j+1]);
		}
	}
	cout<<f[n][n]<<endl;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值