2020年牛客多校第五场C题-easy(纯组合计数不要生成函数的做法)
文章目錄
- description
- solution
- code
description
有TTT組測試數據
對于兩個長度為KKK的數列{a}\{a\}{a}和{b}\{b\}{b},滿足∑i=1Kai=N,∑i=1Kbi=M\sum_{i=1}^Ka_i=N,\sum_{i=1}^Kb_i=M∑i=1K?ai?=N,∑i=1K?bi?=M
對于這兩個數列,定義權值為P=∏i=1Kmin?(ai,bi)P=\prod_{i=1}^K\min(a_i,b_i)P=∏i=1K?min(ai?,bi?)
求所有可能的數列的權值之和∑{a},{b}P\sum_{\{a\},\{b\}}P∑{a},{b}?P
答案對998244353998244353998244353取模
K≤min?(N,M)N,M≤5e5T≤100K\le \min(N,M)\quad N,M\le 5e5\quad T\le 100K≤min(N,M)N,M≤5e5T≤100
solution
雖然代碼非常短,但是要理解簡直要命
我們可以構造一個二維數組c[2][K]c[2][K]c[2][K],要求c[0][i]=c[1][i]c[0][i]=c[1][i]c[0][i]=c[1][i],第i位不能為0
設計的初衷是讓c[0][K]c[0][K]c[0][K]為構造{a}\{a\}{a},c[1][K]c[1][K]c[1][K]為構造{b}\{b\}{b}服務
再構造兩個一維數組A[K],B[K]A[K],B[K]A[K],B[K],第i位可以為0
通過構造出來的這些數組生成最后的{a},{b}\{a\},\{b\}{a},{b}
- a[i]=c[0][i]+A[i]a[i]=c[0][i]+A[i]a[i]=c[0][i]+A[i]
- b[i]=c[1][i]+B[i]b[i]=c[1][i]+B[i]b[i]=c[1][i]+B[i]
定義∑i=1Kc[0][i]=∑i=1Kc[1][i]=S\sum_{i=1}^Kc[0][i]=\sum_{i=1}^Kc[1][i]=S∑i=1K?c[0][i]=∑i=1K?c[1][i]=S
則對于{a}\{a\}{a}序列而言,剩下的N?SN-SN?S就需要由AAA數組來彌補;{b}\{b\}{b}同理
考慮有多少個A/BA/BA/B數組滿足其求和恰好彌補了空缺N?S/M?SN-S/M-SN?S/M?S
顯然這可以通過組合數算出
- A:C(N?S+K?1,K?1)A:C(N-S+K-1,K-1)A:C(N?S+K?1,K?1)
- B:C(M?S+K?1,K?1)B:C(M-S+K-1,K-1)B:C(M?S+K?1,K?1)
以AAA數組的計算方式為例,BBB同理
相當于把剩下的N?SN-SN?S分成恰好KKK個盒子,隔板法,插入K?1K-1K?1個板子
但是AAA的條件可以為000,意味著盒子可以為空,但這是隔板法不能求得的
可以通過給每個盒子分配111的占位相當于一個盒子,等價于將限制變成至少為111
考慮C(N?S+K?1,K?1)?C(M?S+K?1,K?1)C(N-S+K-1,K-1)*C(M-S+K-1,K-1)C(N?S+K?1,K?1)?C(M?S+K?1,K?1)
發現這兩個組合數相乘有非常實際的意義
這求出的恰好是將所有滿足?1≤i≤Kc[0][i]≤a[i]\forall_{1\le i\le K}\ c[0][i]\le a[i]?1≤i≤K??c[0][i]≤a[i]的{a}\{a\}{a}和?1≤i≤Kc[0][i]≤b[i]{b}\forall_{1\le i\le K}\ c[0][i]\le b[i]\{b\}?1≤i≤K??c[0][i]≤b[i]{b}的組合都計算了恰好一次
接著我們考慮對于一組特定的ccc數組和特定的A,BA,BA,B數組(也就是一組特定的{a},{b}\{a\},\{b\}{a},{b})的貢獻
∏i=1Kmin?(ai,bi)\prod_{i=1}^K\min(a_i,b_i)∏i=1K?min(ai?,bi?) 這是題目定義的貢獻
思考一下,這樣一組特定的{a},{b}\{a\},\{b\}{a},{b},會被多少個ccc計算到?
-
顯然是∏i=1Kmin?(ai,bi)\prod_{i=1}^K\min(a_i,b_i)∏i=1K?min(ai?,bi?)
每一位iii都滿足c[0][i]=c[1][i]≤min?(ai,bi)c[0][i]=c[1][i]\le\min(a_i,b_i)c[0][i]=c[1][i]≤min(ai?,bi?)就行,因為ccc要求不能為000,而A,BA,BA,B要求可以為000
將每一位c[i]c[i]c[i]的可取值個數相乘,即是能利用A,BA,BA,B修補成{a},{b}\{a\},\{b\}{a},{b}的所有ccc的組合
于是乎,發現非常巧妙地將答案的乘積和轉化成了計數個數貢獻
再轉個彎,我們直接計算每個特定的ccc會對多少個{a},{b}\{a\},\{b\}{a},{b}提供111的計數貢獻,求和就等價于原問題的總貢獻
然而,又發現了一個非常巧妙的性質,和(S)相同的ccc產生的計數貢獻是一樣的(但不是貢獻的{a},{b}\{a\},\{b\}{a},{b}完全相同的意思)
-
要證明這個性質,就需要再轉個彎,用到最開始的組合數推導
剩下的N?S,M?SN-S,M-SN?S,M?S都需要A,BA,BA,B的補給
A,BA,BA,B怎么補給,最后生成的{a},{b}\{a\},\{b\}{a},{b}會因此而固定
是一對一的關系
這個計數貢獻自然是CS?1,K?1C_{S-1,K-1}CS?1,K?1?
-
插板法
ccc的和為SSS,要求分成KKK個盒子,盒子不能為空
相當于有S?1S-1S?1個空位需要插入K?1K-1K?1個板
最后就成功不用生成函數解決這道題了!!!
現在總結,就只有一句話,最開始的c,A,Bc,A,Bc,A,B構造簡直就是神來之筆
后面所有的推導全都是建立在最開始的規則設定基礎上
真的妙極了!!
code
#include <cstdio> #include <iostream> using namespace std; #define int long long #define mod 998244353 #define maxn 1000005 int fac[maxn], inv[maxn];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }void init( int n ) {fac[0] = inv[0] = 1;for( int i = 1;i <= n;i ++ )fac[i] = fac[i - 1] * i % mod;inv[n] = qkpow( fac[n], mod - 2 );for( int i = n - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod; }int C( int n, int m ) {return fac[n] * inv[m] % mod * inv[n - m] % mod; }signed main() {init( 1e6 );int n, m, k, T;scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld %lld", &n, &m, &k );int ans = 0;for( int i = k;i <= min( n, m );i ++ )ans = ( ans + C( i - 1, k - 1 ) * C( n - i + k - 1, k - 1 ) % mod * C( m - i + k - 1, k - 1 ) ) % mod;printf( "%lld\n", ans );}return 0; }總結
以上是生活随笔為你收集整理的2020年牛客多校第五场C题-easy(纯组合计数不要生成函数的做法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 下载 沙耶之歌Android_沙耶之歌安
- 下一篇: 专题突破之反悔贪心——建筑抢修,Cow