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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

算法刷题-数论-组合数、快速幂、逆元、递推求组合数、逆元求组合数

發布時間:2025/4/5 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法刷题-数论-组合数、快速幂、逆元、递推求组合数、逆元求组合数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • acwing885. 求組合數 I(遞推:數據范圍:2000)
      • acwing875. 快速冪(a的k次方 模 b)
      • acwing876. 快速冪求逆元
      • acwing886. 求組合數 II(逆元:數據范圍:100000)
      • acwing887. 求組合數 III
      • acwing888. 求組合數 IV(不取模,直接算Cab)

acwing885. 求組合數 I(遞推:數據范圍:2000)


分析:組合數Cab=a!b!(a?b)!C_a^b =\frac{a!}{b!(a-b)!}Cab?=b!(a?b)!a!?,比如C53=5!3!(5?3)!=5×42=10C_5^3 =\frac{5!}{3!(5-3)!} =\frac{5 \times 4}{2} =10C53?=3!(5?3)!5!?=25×4?=10,a和b的范圍是2000,按照定義算的話,階乘的復雜度會超時。

可以使用組合數的遞推式:兩重循環時間復雜度O(N2)O(N^2)O(N2)
Cab=Ca?1b+Ca?1b?1C_a^b =C_{a-1}^b + C_{a-1}^{b-1} Cab?=Ca?1b?+Ca?1b?1?

在2000 *2000 的時間內來預處理所有組合數的值,最后求的話直接查表即可。
acwing885. 求組合數 I
AC代碼

#include<bits/stdc++.h> using namespace std; const int N =2010, mod = 1e9 + 7;int c[N][N]; void init(){for(int i =0 ;i< N; i++)for(int j =0; j<=i; j++)if(!j) c[i][j] =1;else c[i][j] = (c[i-1][j] + c[i-1][j-1]) %mod; }int main(){int n;cin >> n;init();while(n--){int a, b;cin>> a >> b;cout<<c[a][b]<<endl;} }

acwing875. 快速冪(a的k次方 模 b)

acwing875. 快速冪

分析:快速冪,顧名思義,快速地求冪akmodba^k mod \ bakmod?b,時間復雜度O(logk)O(logk)O(logk)
常規思路:復雜度O(k),太慢!

int res =1; for(int i =1;i<=k ;i++) res =res * a mod p;

快速冪:復雜度O(logk)
首先初始化
a20modpa21modpa22modp......a2logkmodpa^{2^{0}} mod \ p \\ a^{2^{1}} mod \ p \\a^{2^{2}} mod \ p\\......\\a^{2^{logk}} mod \ pa20mod?pa21mod?pa22mod?p......a2logkmod?p

對于要求的aka^kak,我們需要用上面的拼湊出來 ak=a2i×a2j×.....×a2m=a2i+2j+...+2ma^k =a^{2^{i}}\times a^{2^{j}} \times.....\times a^{2^{m}} =a^{2^i+2^j+...+2^m}ak=a2i×a2j×.....×a2m=a2i+2j+...+2m,這里需要用到k的二進制表示,比如k=10ten=1010twok=10_{ten} =1010_{two}k=10ten?=1010two?,那么a10=a21+23a^{10} =a^{2^1+2^3}a10=a21+23,就是二進制表示中哪一位是1,就可以將指數拆成2的冪的和的形式。

ac代碼

#include<bits/stdc++.h> using namespace std;typedef long long LL;//a^k %p int qmi(int a, int k ,int p){int res = 1;//k的二進制表示while(k){//如果當前k二進制的末位是1的話if(k & 1) res = (LL) res* a % p;//k >>= 1; //k右移1,等價于刪掉最后一位//a的下一個,平方一下,2^0 to 2^1 to 2^2 to 2^3....a = (LL) a * a % p;}return res; }int main(){int n ;scanf("%d",&n);while(n--){int a, k ,p;scanf("%d%d%d",&a,&k,&p);printf("%d\n",qmi(a,k,p));} }

acwing876. 快速冪求逆元

acwing876. 快速冪求逆元

分析:我們想把ab\frac{a}{b}ba?這種除法轉化為乘法運算,這里把ab≡a×x(modp)\frac{a}{b} ≡a\times x (mod\ p)ba?a×x(mod?p),把x稱為b的模p的逆元,記作b?1b^{-1}b?1.這是一個數,不是 -1次方。逆元存在的前提是b和模數p互質。
換句話說,b 的模p逆元x滿足b×x≡1(modp)b \times x ≡1(mod\ p)b×x1(mod?p)

由費馬小定理 bp?1≡1(modp)b^{p-1} ≡1(mod \ p)bp?11(mod?p),則b×bp?2≡1(modp)b\times b^{p-2} ≡1( mod\ p)b×bp?21(mod?p)
由逆元的定義,bp?2b^{p-2}bp?2就是b的模p逆元。

來源:維基百科

所以這道題求得就是ap?2modpa^{p-2} mod\ pap?2mod?p,所以這是一個快速冪的題目。

AC代碼

#include<bits/stdc++.h> using namespace std; typedef long long LL;int qmi(int a,int k,int p){int res =1;while(k){if(k & 1)res = (LL) res*a % p;k >>= 1;a =(LL) a* a % p;}return res; }int main(){int n;cin >> n;while(n--){int a ,p;cin >> a >> p;//互質的情況下,才有逆元if(a % p ) cout<<qmi(a,p-2, p)<<endl;//不互質else cout<<"impossible"<<endl;} }

acwing886. 求組合數 II(逆元:數據范圍:100000)

這里的數據量a和b是100000(10萬),采用的優化是對組合數Cab=a!b!(a?b)!C_a^b =\frac{a!}{b!(a-b)!}Cab?=b!(a?b)!a!? 中某一步的優化,求逆元infact(b-a),簡單地講,可以把逆元看作倒數。

所以組合數Cab=a!b!(a?b)!=fact(a)×infact(b?a)×infact(b)C_a^b =\frac{a!}{b!(a-b)!} =fact(a) \times infact(b-a)\times infact(b)Cab?=b!(a?b)!a!?=fact(a)×infact(b?a)×infact(b)

fact[i]=i!mod109+7fact[i] = i ! \mod \ 10^9+7fact[i]=i!mod?109+7
infact[i]=(i!)?1mod109+7infact[ i] =(i !)^{-1} mod \ 10^9 +7infact[i]=(i!)?1mod?109+7

時間復雜度O(NlogN)O(NlogN)O(NlogN)

ac代碼

#include<bits/stdc++.h> using namespace std; const int N = 100010, mod = 1e9 +7;typedef long long LL;int fact[N],infact[N];//快速冪int qmi(int a, int k ,int p){int res =1;while(k){if(k & 1) res = (LL) res * a % p;a = (LL) a* a %p;k>>= 1;}return res; }int main(){fact[0] = infact[0] =1;for(int i=1; i< N; i++){//階乘預處理,打表fact[i] =(LL)fact[i -1] * i % mod;//逆元 i 的 mod-2次方就是 i的模 mod的逆元infact[i] = (LL)infact[i-1]* qmi(i ,mod -2 ,mod) % mod;}int n;cin >> n;while(n--){int a ,b ;cin >> a >> b;//階乘定義計算cout<< (LL)fact[a] *infact[b] %mod * infact[a-b] % mod<<endl;} }

acwing887. 求組合數 III

acwing887. 求組合數 III
數據范圍 1e18,使用盧卡斯定理時間復雜度大概是O(plogp)O(plogp)O(plogp)

盧卡斯定理
Cab≡Camodpbmodp×Ca/pb/p(modp)C_{a}^{b} ≡ C_{a \ mod \ p}^{b \ mod \ p}\times C_{a/p}^{b/p}(mod\ p)Cab?Ca?mod?pb?mod?p?×Ca/pb/p?(mod?p)

ac代碼

#include<bits/stdc++.h> using namespace std;typedef long long LL;int p;//快速冪 int qmi(int a, int k){int res =1;while(k){if(k& 1) res =(LL) res * a % p;a =(LL) a*a % p;k >>= 1;}return res; }//返回組合數:a里面選b個數 int C(int a ,int b){int res = 1;for(int i =1 , j= a; i<= b; i++ ,j --){//組合數中的分子res = (LL) res * j % p;//組合數中的分母res = (LL) res * qmi(i ,p-2) % p;}return res; }//lucas定理 int lucas( LL a ,LL b){if(a<p && b< p) return C(a,b);return (LL)C(a %p ,b % p) *lucas(a/p ,b/p) % p; } int main(){int n;cin >> n;while(n--){LL a , b;cin>> a >> b >> p;cout<<lucas(a, b)<<endl;} }

acwing888. 求組合數 IV(不取模,直接算Cab)


acwing888. 求組合數 IV

Cab=a!(a?b)!b!C_{a}^{b} =\frac{a!}{(a-b)!b!}Cab?=(a?b)!b!a!?
思路
1.篩素數
2.每個質數的次數
a!=?ap?+?ap2?+?ap3?+...a! =\lfloor\frac{a}{p} \rfloor +\lfloor\frac{a}{p^2} \rfloor+\lfloor\frac{a}{p^3} \rfloor+...a!=?pa??+?p2a??+?p3a??+...
3.高精度乘法所有質因子乘在一起

AC代碼

#include<bits/stdc++.h> using namespace std; const int N = 5010; int primes[N],sum[N],cnt; bool st[N];//線性篩質數void get_prime(int n){for(int i =2; i<= n; i++){if(!st[i]) primes[cnt++] =i;for(int j =0;primes[j]<= n/i; j++){st[primes[j] * i] =true;if( i % primes[j] == 0) break;}} } //有幾個p int get(int n ,int p){int res = 0;while(n){res += n/ p;n/= p;}return res; }//高精度乘法 vector<int> mul(vector<int> a,int b){vector<int> c;int t =0;for(int i= 0; i<a.size();i++){t += a[i]*b;c.push_back(t % 10);t/= 10;}while(t){c.push_back(t %10);t /= 10;}return c; } int main(){int a, b;cin >> a >> b;get_prime(a);for(int i =0; i < cnt; i++){int p =primes[i];sum[i] = get(a,p) -get(b,p) -get(a-b,p); }vector<int > res;res.push_back(1);for(int i=0 ;i< cnt; i++){for(int j = 0; j<sum[i]; j++)res = mul(res, primes[i]);}for(int i= res.size()-1; i>= 0; i--)cout<<res[i];}

總結

以上是生活随笔為你收集整理的算法刷题-数论-组合数、快速幂、逆元、递推求组合数、逆元求组合数的全部內容,希望文章能夠幫你解決所遇到的問題。

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