1. 程式人生 > 其它 >Atcoder Regular Contest 061 D - Card Game for Three(組合數學)

Atcoder Regular Contest 061 D - Card Game for Three(組合數學)

組合數學+遞推求解難以用組合恆等式變形的柿子

洛谷題面傳送門 & Atcoder 題面傳送門

首先考慮合法的排列長什麼樣,我們考慮將每次操作者的編號記錄下來形成一個序列(第一次 A 操作不計入序列),那麼顯然這個序列中必須恰好含有 \(n\) 個 A,且最後一個必須是 A。那麼顯然一個合法的取卡片方案唯一對應一個操作序列,而一個長度為 \(l\) 的操作序列恰好對應 \(3^{n+m+k-l}\) 個合法的取卡片方案(證明?就每次下一輪執行操作的人是誰就在對應的操作者所取的卡片上寫啥,那麼顯然對於長度為 \(l\) 的操作序列而言,在原卡片堆中恰好有 \(l\) 張卡片上的數是確定下來的,另外 \(n+m+k-l\) 張可以 randomly 亂填,方案數就是 \(3^{n+m+k-l}\)

然後考慮怎樣統計方案,我們列舉操作序列中 BC 出現次數 \(c\),那麼操作序列長度即為 \(c+n\),其中有一個 A 的位置已經確定了,那麼填好另外 \(n-1\) 個 A 的方案數即是 \(\dbinom{n+c-1}{n-1}\),如果我們能求出填好 B、C 的方案數 \(f_k\),就有 \(ans=\sum\limits_{c=0}^{m+k}\dbinom{n+c-1}{n-1}f_c·3^{m+k-c}\)

接下來考慮怎樣求 \(f_c\),首先列出柿子,列舉 B 的個數然後組合數統計答案,即

\[f_c=\sum\limits_{i}\dbinom{c}{i}[i\le m][c-i\le k] \]

上式也可以寫作

\[f_c=\sum\limits_{c-k\le i\le m}\dbinom{c}{i} \]

我們知道組合數下底數求和是無法直接求的,不過注意到組合數有個遞推公式 \(\dbinom{n}{k}=\dbinom{n-1}{k}+\dbinom{n-1}{k-1}\),因此考慮從數列遞推的角度理解這道題,即:

\[\begin{aligned} f_c&=\sum\limits_{c-k\le i\le m}\dbinom{c}{i}\\ &=\sum\limits_{c-k\le i\le m}\dbinom{c-1}{i}+\dbinom{c-1}{i-1}\\ &=\sum\limits_{c-k\le i\le m}\dbinom{c-1}{i}+\sum\limits_{c-k\le i\le m}\dbinom{c-1}{i-1}\\ &=\sum\limits_{c-k\le i\le m}\dbinom{c-1}{i}+\sum\limits_{c-k-1\le i\le m-1}\dbinom{c-1}{i}\\ &=2\sum\limits_{c-1-k\le i\le m}\dbinom{c-1}{i}-\dbinom{c-1}{m}-\dbinom{c-1}{c-k-1}\\ &=2f_{c-1}-\dbinom{c-1}{m}-\dbinom{c-1}{c-k-1} \end{aligned} \]

遞推求一下即可,時間複雜度線性。

const int MAXN=9e5; 
const int MOD=1e9+7;
int n,m,k,fac[MAXN+5],ifac[MAXN+5],f[MAXN+5],pw3[MAXN+5];
void init_fac(int n){
	for(int i=(fac[0]=ifac[0]=ifac[1]=pw3[0]=1)+1;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-1]*ifac[i]%MOD;
	for(int i=1;i<=n;i++) pw3[i]=3ll*pw3[i-1]%MOD;
}
int binom(int x,int y){
	if(x<0||y<0||x<y) return 0;
	return 1ll*fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;
}
int main(){
	scanf("%d%d%d",&n,&m,&k);init_fac(n+m+k);
	f[0]=1;for(int i=1;i<=m+k;i++)
		f[i]=(2ll*f[i-1]-binom(i-1,m)-binom(i-1,i-k-1)+MOD*2ll)%MOD;
	int ans=0;for(int i=0;i<=m+k;i++) ans=(ans+1ll*pw3[m+k-i]*binom(n-1+i,i)%MOD*f[i])%MOD;
	printf("%d\n",ans);
	return 0;
}