#include<iostream> #include<algorithm> #include<string> #include<cstring> using namespace std; long long C(int m, int n) { int k = 1;// 相當於 C(m,n) long long ans=1; while(k<=n) { ans=((m-k+1)*ans)/k; k++; } return ans; } int main() { printf("%lld", C(10 ,5)); }
方法二:楊輝三角打表
原理:C(n,m)=C(n-1,m-1)+C(n-1,m)
#include<iostream> using namespace std; int n,m; long long c[10005]; int main() { cin>>n>>m; m=min(m,n-m);//因為C[n][m]=C[n][n-m],所以m可以取m和n-m中小的那一個,以節省時間。 c[0]=1; for (int i=1;i<=n;i++) { for (int j=m;j>=1;j--) { c[j]=c[j]+c[j-1]; } } cout<<c[m]; }
求解思路:
1. 篩法求出范圍內的所有質數。
2. 通過 C(n, m) = n! / m! / (n - m)! 這個公式求出每個質因子的次數。 n! 中p的次數是 n / p + n / p^2 + n / p^3 + ...
3. 用高精度乘法將所有質因子相乘。
代碼
#include<iostream> #include<algorithm> #include<vector> using namespace std; int prime[1000],cnt=0,rat[1000]; bool p[1000]; void findprime(int n)//歐拉篩來找素數 { for (int i = 2; i <= n; i++) { if (!p[i]) prime[cnt++] = i; for (int j = 0; j < cnt; j++) { if (i*prime[j] > n)break; p[i*prime[j]] = true; if (i%prime[j] == 0)break; } } } int rate(int n, int p)//分解質因數,求得每個質因數在n!中的出現個數 { int res = 0; while (n) { res += n / p; n /= p; } return res; } vector<int>mul(vector<int>a, int b)//高精度乘法*低精度數字(此時的a一直是反着的,所以最后反着輸出) { vector<int>answer; int t=0;//進位數 for (int i = 0; i < a.size(); i++) { t += a[i] * b; answer.push_back(t % 10); t /= 10; } while (t)//處理最高位的數字,防止最高位的數字大於9 { answer.push_back(t % 10); t /= 10; } return answer; } int main() { int n, m; scanf("%d %d", &n, &m); findprime(n); for (int i = 0; i < cnt; i++) rat[i] = rate(n, prime[i]) - rate(m, prime[i]) - rate(n - m, prime[i]);//計算每個質因數的冪的次數 vector<int>fin; fin.push_back(1); for (int i = 0; i < cnt; i++)//所有質因數的迭代 for (int j = 0; j < rat[i]; j++)// rat[i]是每個質因數在合數中的次數,所以這里是將每個質因數乘rat[i]次 fin = mul(fin, prime[i]); for (int i = fin.size() - 1; i >= 0; i--) printf("%d", fin[i]); }
1.歐拉篩來找素數
如果對歐拉篩不熟悉,點擊此處查看。
2.分解質因數,求得每個質因數在n!中的出現個數
求N!中素因子p的個數,也就是N!中p的冪次
公式為:cnt=[n/p]+[n/p^2]+[n/p^3]+...+[n/p^k];
例如:N=12,p=2
12/2=6,表示1~12中有6個數是2的倍數,即2,4,6,8,10,12
12/2^2=6/2=3,表示1~12中有3個數是4的倍數,即4,8,12,它們能在提供2的基礎上多提供一個2
12/2^3=3/2=1,表示1~12中有1個數是8的倍數,即12,它能在提供兩個2的基礎上又多提供一個2
代碼就是
int rate(int n, int p)//分解質因數,求得每個質因數在n!的出現個數 { int res = 0; while (n) { res += n / p; n /= p; } return res; }
效果:
例如求c(5,3),則通過代碼首先要計算5的質因數:2 3 5;
之后分別計算每個質因數在n中的出現個數:rate(2)=3;rate(3)=1;rate(5)=1;而5!=5 * 4 * 3 * 2 *1;其中將合數轉換為素數,則2出現的個數就是rate(2)的值:3次(一個4,一個2,相當於3個2),3、5的出現次數各為1
而每個質因數在n!的出現的個數就是每個質因數在n!中對應的冪數
3.使用C(n, m) = n! / m! / (n - m)! 這個公式求出每個質因子的次數
我們上一步計算出了每個質因數在n!中的冪數,而類比就能求出在m!、(n-m)!中的冪數,而C(n, m) = n! / m! / (n - m)! 這個式子的值就是各個質因子在n!中的冪數-在m!中的冪數-在(n-m)!中的冪數,即
for (int i = 0; i < cnt; i++) rat[i] = rate(n, prime[i]) - rate(m, prime[i]) - rate(n - m, prime[i]);//計算每個質因數的冪的次數
4.高精度乘法計算結果
至此,我們將C(n, m)的值轉換為了多個質因子的若干冪次方的乘積,我們通過高精度*低精度來計算(大數運算知識:請點擊此處了解)
vector<int>mul(vector<int>a, int b)//高精度乘法*低精度數字(此時的a一直是反着的,所以最后反着輸出) { vector<int>answer; int t=0;//進位數 for (int i = 0; i < a.size(); i++) { t += a[i] * b; answer.push_back(t % 10); t /= 10; } while (t)//處理最高位的數字,防止最高位的數字大於9 { answer.push_back(t % 10); t /= 10; } return answer; }
在大數的乘法計算中,首先是要把原來的大數求逆后再從前往后計算,最后再將答案逆序輸出,但是此時看似vector<int>
其實這里的原因是vector<int>a的初始化其實是1,這里的1要理解成已經是求逆后的(單個數字求逆還是原數)
例如:初始a中是1,令b=5;開始1 *5 =5;vector<int>p中是5
再調用mul函數,就是5 *5 =25;此時vector<int>p中是52
再調用mul函數,就是25 *5 =125;此時vector<int>p中是521
最后逆序輸出就是125
for (int i = 0; i < cnt; i++)//所有質因數的迭代 for (int j = 0; j < rat[i]; j++)// rat[i]是每個質因數在合數中的次數,所以這里是將每個質因數乘rat[i]次 fin = mul(fin, prime[i]);
最后將所有的質因子都乘rat[i]遍,再去乘別的質因子,最后將結果逆序輸出就是C(n, m)的值
https://blog.csdn.net/weixin_33895016/article/details/94615900