1. 程式人生 > 其它 >Character Encoding(組合數學&隔板法&容斥原理)

Character Encoding(組合數學&隔板法&容斥原理)

題目:https://vjudge.z180.cn/problem/HDU-6397

題意:給三個數n,m,k表示可以從0到n-1選數,選出m個數使他們的加和等於k,例如選三個數使它們的和等於1,1-0-0和0-0-1是不同方案,問能夠組成k的方案數。

題解:

首先不考慮n,這裡的k看成k個小球,然後放到m個盒子裡,就可以使用隔板法,可以為空的話就是答案就是$C_{x+m-1}^{m-1}\textrm{}$,但是這裡還有一個n的限制,就是當單個盒子面放的>=n是不符合的,這部分需要減掉,這裡用容斥原理來做。
根據$C_{x+m-1}^{m-1}\textrm{}$可知,這裡面需要去掉的是有一個盒子>=n,有兩個盒子>=n,有三個盒子>=n等等,首先計算有一個盒子>=n的情況,就是$C_{m}^{1}\textrm{}$ * $C_{x+m-1-1*n}^{m-1}\textrm{}$,這個式子代表從m個盒子裡面任選一個盒子讓它放n個,然後剩下的在m個盒子裡面任意放,就是後面那個式子。當然,這裡可能不止有一個盒子超過n,而且在這裡面有兩個>=n的盒子是計算了倆次,也就是減去了倆次,所以就要加上$C_{m}^{2}\textrm{}$ * $C_{x+m-1-2*n}^{m-1}\textrm{}$,這樣就加上了任選倆個>=n的一次,就相當於在總數上減去了1次,就是正確的了。當然對於$C_{m}^{1}\textrm{}$ * $C_{x+m-1-1*n}^{m-1}\textrm{}$任選一個盒子>=n是隻計算了一次,直接減掉沒問題。對於任選3個盒子大於等於n,$C_{m}^{1}\textrm{}$ * $C_{x+m-1-1*n}^{m-1}\textrm{}$就算了3次,減掉了3次,$C_{m}^{2}\textrm{}$ * $C_{x+m-1-2*n}^{m-1}\textrm{}$加上了三次,$C_{m}^{3}\textrm{}$ * $C_{x+m-1-3*n}^{m-1}\textrm{}$減去一次,所以和就是在總的基礎上減去了1次,是正確的,同理對於任選4個盒子>=n等等,這麼計算下去就是答案。

總結:s=$C_{x+m-1}^{m-1}\textrm{}$
1.任選1個盒子>=n是s-$C_{m}^{1}\textrm{}$ * $C_{x+m-1-1*n}^{m-1}\textrm{}$,這樣任選<=1個盒子,每個盒子>=n就是對的
2.任選兩個盒子>=n是s-$C_{m}^{1}\textrm{}$ * $C_{x+m-1-1*n}^{m-1}\textrm{}$ +$C_{m}^{2}\textrm{}$ * $C_{x+m-1-2*n}^{m-1}\textrm{}$這樣任選<=2個盒子,每個盒子>=n就是對的
3.任選三個盒子>=n是s-$C_{m}^{1}\textrm{}$ * $C_{x+m-1-1*n}^{m-1}\textrm{}$+$C_{m}^{2}\textrm{}$ * $C_{x+m-1-2*n}^{m-1}\textrm{}$ - $C_{m}^{3}\textrm{}$ * $C_{x+m-1-3*n}^{m-1}\textrm{}$
這樣任選<=3個盒子,每個盒子>=n就是對的
...
一個for迴圈掃一遍就行

公式進一步解釋:
對於$C_{x+m-1-num*n}^{m-1}\textrm{}$,num是任選幾個盒子>=n,設ans為任選幾個盒子>=n,當然這裡的ans>=num,然後$C_{ans}^{ans-num}\textrm{}$是num那個狀態公式對ans狀態的影響,即重複了幾次。

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include 
<deque> #include <functional> #include <iostream> #include <map> #include <queue> #include <set> #include <stack> #include <string> #include <vector> //#include <unordered_map> //#include <unordered_set> //#include <bits/stdc++.h> //#define int long long #define pb push_back #define PII pair<int, int> #define mpr make_pair #define ms(a, b) memset((a), (b), sizeof(a)) #define x first #define y second typedef long long ll; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 1e6 + 7; const int mod = 998244353; using namespace std; int fact[N], infact[N]; int quai(int a, int b) { //求逆元 int s = 1; while (b) { if (b & 1) s = (ll)s * a % mod; b = b / 2; a = (ll)a * a % mod; } return s; } void init(int n) { //組合數 fact[0] = 1, infact[0] = 1; for (int i = 1; i <= n; i++) { fact[i] = (ll)fact[i - 1] * i % mod; infact[i] = (ll)infact[i - 1] * quai(i, mod - 2) % mod; } } int C(int a, int b) { //組合數 if (a < b) return 0; return (ll)fact[a] * infact[b] % mod * infact[a - b] % mod; } // signed main(){ int main(int argc, char const *argv[]) { int T; init(1e6); scanf("%d", &T); while (T--) { int n, m, x; scanf("%d%d%d", &n, &m, &x); ll s = C(m + x - 1, m - 1); //總的s int f = -1; //掃一遍 for (int i = 1; i * n <= x; i++) { s = (s +(ll)f * C(m, i) * C((ll)m + x - 1 - (ll)i * n, m - 1) % mod + mod) % mod; f *= -1; } printf("%lld\n", s % mod); } return 0; }
View Code