日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

组合数取模

發布時間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 组合数取模 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

組合數取模在ACM競賽中是一個很重要的問題,很多選手因為數據太大而束手無策,今天就來詳細講解它。

?

組合數取模就是求的值,當然根據,和的取值范圍不同,采取的方法也不一樣。

?

接下來,我們來學習一些常見的取值情況

?

(1)

?

???? 這個問題比較簡單,組合數的計算可以靠楊輝三角,那么由于和的范圍小,直接兩層循環即可。

?

(2)和,并且是素數

?

???? 這個問題有個叫做Lucas的定理,定理描述是,如果

?

????

?

?????那么得到

?

????

???

???? 這樣然后分別求,采用逆元計算即可。

?

?

題目:http://acm.fzu.edu.cn/problem.php?pid=2020

?

題意:求,其中,并且是素數。

?

代碼:

#include <iostream> #include <string.h> #include <stdio.h>using namespace std; typedef long long LL;LL n,m,p;LL quick_mod(LL a, LL b) {LL ans = 1;a %= p;while(b){if(b & 1){ans = ans * a % p;b--;}b >>= 1;a = a * a % p;}return ans; }LL C(LL n, LL m) {if(m > n) return 0;LL ans = 1;for(int i=1; i<=m; i++){LL a = (n + i - m) % p;LL b = i % p;ans = ans * (a * quick_mod(b, p-2) % p) % p;}return ans; }LL Lucas(LL n, LL m) {if(m == 0) return 1;return C(n % p, m % p) * Lucas(n / p, m / p) % p; }int main() {int T;scanf("%d", &T);while(T--){scanf("%I64d%I64d%I64d", &n, &m, &p);printf("%I64d\n", Lucas(n,m));}return 0; }


由于上題的比較大,所以組合數只能一個一個計算,如果的范圍小點,那么就可以進行階乘預處理計算了。

?

(3)和,并且可能為合數

?

????這樣的話先采取暴力分解,然后快速冪即可。

?

題目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=628

?

代碼:

#include <iostream> #include <string.h> #include <stdio.h>using namespace std; typedef long long LL; const int N = 200005;bool prime[N]; int p[N]; int cnt;void isprime() {cnt = 0;memset(prime,true,sizeof(prime));for(int i=2; i<N; i++){if(prime[i]){p[cnt++] = i;for(int j=i+i; j<N; j+=i)prime[j] = false;}} }LL quick_mod(LL a,LL b,LL m) {LL ans = 1;a %= m;while(b){if(b & 1){ans = ans * a % m;b--;}b >>= 1;a = a * a % m;}return ans; }LL Work(LL n,LL p) {LL ans = 0;while(n){ans += n / p;n /= p;}return ans; }LL Solve(LL n,LL m,LL P) {LL ans = 1;for(int i=0; i<cnt && p[i]<=n; i++){LL x = Work(n, p[i]);LL y = Work(n - m, p[i]);LL z = Work(m, p[i]);x -= (y + z);ans *= quick_mod(p[i],x,P);ans %= P;}return ans; }int main() {int T;isprime();cin>>T;while(T--){LL n,m,P;cin>>n>>m>>P;n += m - 2;m--;cout<<Solve(n,m,P)<<endl;}return 0; }


?

接下來看一些關于組合數取模的典型題目。

?

題目:http://acm.hdu.edu.cn/showproblem.php?pid=3944

?

分析:組合數取模的典型題目,用Lucas定理,注意要階乘預處理,否則會TLE的。

?

?

題目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4536

?

題意:給一個集合,一共個元素,從中選取個元素,選出的元素中沒有相鄰的元素的選法一共有多少種?

?

分析:典型的隔板法,最終答案就是。然后用Lucas定理處理即可。

?

?

題目:http://acm.hdu.edu.cn/showproblem.php?pid=4373

?

題意:for循環嵌套,有兩種形式,第一類從1開始到,第二類從上一層循環當前數開始到,第一層一定

?????是第一種類型,求總的循環的次數對364875103取余的結果。

?

分析:首先可以看出,每一個第一類循環都是一個新的開始,與前面的狀態無關,所以可以把個嵌套分為幾個不

?????同的部分,每一個部分由第一類循環開始,最終結果相乘即可。剩下的就是第二類循環的問題,假設一個

???? 層循環,最大到,分析一下得到如下結果

????

?????(1)只有一層,則循環次數為

?

?????(2)只有前兩層,則循環次數為

?

?????????

?

?????(3)只有前三層,則循環次數為

?

????????

?

??????由此得到結論:第的循環次數為

?

代碼:

#include <iostream> #include <string.h> #include <stdio.h>using namespace std; typedef long long LL;const int N = 25; const int MOD1 = 97; const int MOD2 = 3761599; const int MOD = MOD1 * MOD2;int m,n,k; int a[N]; LL fac1[MOD1+10]; LL fac2[MOD2+10]; LL inv1,inv2;LL quick_mod(LL a,LL b,LL m) {LL ans = 1;a %= m;while(b){if(b & 1){ans = ans * a % m;b--;}b >>= 1;a = a * a % m;}return ans; }LL C(LL n,LL m,LL p,LL fac[]) {if(n < m) return 0;return fac[n] * quick_mod(fac[m] * fac[n-m], p - 2, p) % p; }LL Lucas(LL n,LL m,LL p,LL fac[]) {if(m == 0) return 1;return C(n % p, m % p, p, fac) * Lucas(n / p, m / p, p, fac); }void Init() {fac1[0] = fac2[0] = 1;for(int i=1; i<MOD1; i++)fac1[i] = (fac1[i-1] * i) % MOD1;for(int i=1; i<MOD2; i++)fac2[i] = (fac2[i-1] * i) % MOD2;inv1 = MOD2 * quick_mod(MOD2, MOD1-2, MOD1);inv2 = MOD1 * quick_mod(MOD1, MOD2-2, MOD2); }int main() {Init();int T, tt = 1;scanf("%d",&T);while(T--){scanf("%d%d%d",&n,&m,&k);for(int i=0; i<k; i++)scanf("%d",&a[i]);a[k] = m;LL ans = 1;for(int i=0; i<k; i++){LL m1 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD1, fac1);LL m2 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD2, fac2);LL mm = (m1 * inv1 + m2 * inv2) % MOD;ans = ans * mm % MOD;}printf("Case #%d: ",tt++);cout<<ans<<endl;}return 0; }


?

題目:http://acm.hdu.edu.cn/showproblem.php?pid=4349

?

題意:中有多少個奇數,其中。

?

分析:其實組合數判斷奇偶性有一個優美的結論

??????????

??????????? 如果,那么為奇數,否則為偶數

?

????????????當然本題要判斷的組合數很多,所以不能用上述結論,只能另辟蹊徑。由于是判斷奇偶性,那么就是判斷

?????是否為1,利用Lucas定理,先把和化為二進制,這樣它們都是01序列了。我們又知道

?????。這樣中為0的地方對應的中的位置只有一種可能,那就是0。

?

????? 這樣我們可以不用管中為0的地方,只考慮中為1的位置,可以看出,中為1的位置對應的中為0

????? 或1,其結果都是1,這樣答案就是:1<<(二進制表示中1的個數)

?

代碼:

#include <iostream> #include <string.h> #include <stdio.h>using namespace std;int main() {int n;while(scanf("%d",&n)!=EOF){int cnt = 0;while (n){if (n & 1) cnt++;n >>= 1;}printf("%d\n",1<<cnt);}return 0; }

?

題目:http://61.187.179.132/JudgeOnline/problem.php?id=1951

?

題意:給定兩個正整數和,其中,求下面表達式的值

?

????

?

分析:由于999911659是素數,用費馬小定理降冪得到

?

?????

?

???? 現在關鍵是求

????

????

?

???? 那么我們枚舉分別計算,但是模的是合數,所以對999911658進行分解得到

?

???? ,那么分別求,即

?

?????

?

?????然后進一步得到同余方程組為

?

??????

?

?????再通過中國剩余定理(CRT)可以求得最終答案。

?

代碼:

#include <iostream> #include <string.h> #include <stdio.h>using namespace std; typedef long long LL;const int P = 999911659;LL a[5] = {0, 0, 0, 0}; LL m[5] = {2, 3, 4679, 35617}; LL fac[5][36010]; LL N, G;void Init() {for(int i=0; i<4; i++){fac[i][0] = 1;for(int j=1; j<36010; j++)fac[i][j] = fac[i][j-1] * j % m[i];} }LL quick_mod(LL a,LL b,LL m) {LL ans = 1;a %= m;while(b){if(b & 1){ans = ans * a % m;b--;}b >>= 1;a = a * a % m;}return ans; }LL C(LL n,LL k,int cur) {LL p = m[cur];if(k > n) return 0;return fac[cur][n] * quick_mod(fac[cur][k] * fac[cur][n-k], p - 2, p) % p; }LL Lucas(LL n,LL k,int cur) {LL p = m[cur];if(k == 0) return 1;return C(n % p, k % p, cur) * Lucas(n / p, k / p, cur) % p; }void extend_Euclid(LL a, LL b, LL &x, LL &y) {if(b == 0){x = 1;y = 0;return;}extend_Euclid(b, a % b,x, y);LL tmp = x;x = y;y = tmp - a / b * y; }LL RemindChina(LL a[],LL m[],int k) {LL M = 1;LL ans = 0;for(int i=0; i<k; i++)M *= m[i];for(int i=0; i<k; i++){LL x, y;LL Mi = M / m[i];extend_Euclid(Mi, m[i], x, y);ans = (ans + Mi * x * a[i]) % M;}if(ans < 0)ans += M;return ans; }int main() {Init();while(cin>>N>>G){a[0] = a[1] = 0;a[2] = a[3] = 0;if(G == P){cout<<"0"<<endl;continue;}G %= P;for(int i=1; i*i <= N; i++){if(N % i == 0){LL x = i;a[0] = (a[0] + Lucas(N, x, 0)) % m[0];a[1] = (a[1] + Lucas(N, x, 1)) % m[1];a[2] = (a[2] + Lucas(N, x, 2)) % m[2];a[3] = (a[3] + Lucas(N, x, 3)) % m[3];x = N / i;if(i * i != N){a[0] = (a[0] + Lucas(N, x, 0)) % m[0];a[1] = (a[1] + Lucas(N, x, 1)) % m[1];a[2] = (a[2] + Lucas(N, x, 2)) % m[2];a[3] = (a[3] + Lucas(N, x, 3)) % m[3];}}}LL ans = quick_mod(G, RemindChina(a, m, 4), P);cout<<ans<<endl;}return 0; }


?

題目:已知有如下表達式

?

????

?

????? 給定和,求。

?

分析:如果直接二項式展開,這樣會很麻煩,而且不容易求出,本題有技巧。做如下變換

?

?????

??

?????所以問題變為求的值。?????

?

總結

以上是生活随笔為你收集整理的组合数取模的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。