求组合数 I:
注意:1~1w组数据
#include<iostream>
using namespace std;const int mod = 1e9+7;
long long f[2010][2010];int main()
{int n;scanf("%d",&n);//预处理for(int i=0;i<=2000;i++){for(int j=0;j<=i;j++){if(!j) f[i][j]=1;else f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;//如上图公式所得}}while(n--){int a,b;scanf("%d%d",&a,&b);printf("%lld\n",f[a][b]);}
}
求组合数 II:
注意:1~10w组数据
#include<iostream>
using namespace std;const int mod=1e9+7,N=1e5+10;
typedef long long LL;
long long fac[N],infac[N];//fac[N]阶乘mod p的值,infac[N]阶乘的逆元mod p的值int quick_pow(int a, int k, int p)//快速幂求逆元
{int res = 1;while (k)//对k进行二进制化,从低位到高位{//如果k的二进制表示的第0位为1,则乘上当前的aif (k & 1) res = (LL)res * a % p;//k右移一位k >>= 1;//更新aa = (LL)a * a % p;}return res;//a:4 b:3 p:9
//二进制b 11 1//res 1*4%9=4 4*7%9=1 //b 1 0 //a 4*4%9=7 16*16%9=112//因为:2^0=1,2^1=2,2^2=4,2^3=8...//根据公式:b=3,所以2^0+2^1=b,与这个指数上面的b算的一样//所以(4^2^0%9*4^2^1%9)%9=(4*7)%9=28%9=1,这个就是上面的res
}int main()
{int n;fac[0]=infac[0]=1;for(int i=1;i<=1e5;i++){fac[i]=fac[i-1]*i%mod;//i的阶乘就是上一个阶乘*iinfac[i]=(LL)infac[i - 1] * quick_pow(i,mod-2,mod)%mod;//i的逆元阶乘就是上一个数的逆元阶乘*i的逆元}scanf("%d",&n);while(n--){int a,b;scanf("%d%d",&a,&b);printf("%lld\n",(LL)fac[a] * infac[b] % mod * infac[a - b] % mod);//由上图公式得}
}
求组合数 III:
注意:1~20w组数据
#include<iostream>
#include<algorithm>using namespace std;typedef long long LL;int qmi(int a,int k,int p)//快速幂求逆元
{int res = 1;while(k){//如果k的二进制表示的第0位为1,则乘上当前的aif(k&1)res = (LL)res*a%p;//k右移一位k>>=1;//更新aa = (LL)a*a%p;}return res;
}int C(int a,int b,int p)//自变量类型int
{if(b>a)return 0;//边界条件int res = 1;// a!/(b!*(a-b)!) = (a-b+1)*...*a / b! 分子有b项,这是组合公式for(int i=1,j=a;i<=b;i++,j--)//图二所示公式,递推的每次乘a然后除以b, 因为从a到a−b+1, 所以就是乘b次{res = (LL)res*j%p;//乘a,此时a就是jres = (LL)res*qmi(i,p-2,p)%p;//除以b,就是*b的逆元,此时i就是b}return res;
}
//对公式敲
int lucas(LL a,LL b,int p)
{if(a<p && b<p)return C(a,b,p);//lucas递归终点是C()return (LL)lucas(a/p,b/p,p)*C(a%p,b%p,p)%p;//这是lucas公式,也是最后需要的答案,a%p后肯定是<p的,所以可以用C(),但a/p后不一定<p 所以用lucas继续递归
}int main()
{int n;scanf("%d",&n);while(n--){LL a,b;int p;scanf("%lld%lld%d",&a,&b,&p);printf("%d\n",lucas(a,b,p));}return 0;
}
求组合数 IV:
注意:答案可能很大,需要使用高精度计算
#include<iostream>
#include<algorithm>
#include<vector>using namespace std;const int N=5010;int primes[N],cnt;//primes[N]用来存放质数,cnt代表质数的个数
int sum[N];//每个质数的次方
bool st[N];//st[i], i为质数则为false否则为true,默认全是质数void get_primes(int n)//线性筛质数,即筛出每个p
{for(int i=2;i<=n;i++)//i是某个数的意思,要找2~n的所有数是否为质数{if(!st[i])primes[cnt++]=i;//如果i该数是质数,则放进数组,cnt++代表质数个数加一for(int j=0;primes[j]<=n/i;j++)//把小于n的合数都筛了{st[primes[j]*i]=true;//i无论是质数还是合数,它的质数倍数都可以赋true//例:i=2时,找到i的最小质因子为2,所以2*2=4被赋true// i=3时,找到i的最小质因子为3,所以3*3=9被赋true// i=4时,前面已经找到4为合数所以不会让上面的if出错//像这样,不会让任何i和合数的情况下未被赋true,而导致上面的if出错//当primes[j]是i的最小质因子,说明primes[j]一定是i的倍数primes[j]*i的最小质因子//则说明对于i的倍数primes[j+k]*i的最小质因子也是primes[j],而非primes[j+k]//后面的任何i的倍数都不用看了,直接break,让i++找下一个数是否为质数//当i%primes[j]!=0时,说明此时遍历到的primes[j]不是i的质因子,所以primes[j]*i的最小质因子不是primes[j]//可以继续找primes[j+k]的i倍是否为最小质因子if(i%primes[j]==0)break;}}
}
//计算n的阶乘中<=n的p的指数最大为多少,如2,2^2,2^3...,算最大为几次方
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() || t; i++)//只要有进位或者A没读完,就继续{if (i < a.size())t += a[i] * b;//乘法,A是低位在前,所以先算的低位c.push_back(t % 10);//把余数放进来t /= 10;//给下一位的进位}return c;
}int main()
{int a,b;scanf("%d%d",&a,&b);get_primes(a);//找到1~a的所有质数for(int i=0;i<cnt;i++)//把所有的质数算出a的阶乘中该质数的最大次方为多少{int p = primes[i];//当前i这个数包含的p的个数=a的阶乘的p的个数-(a-b)的阶乘的p的个数-b的阶乘的p的个数//用分子里面 p 的次方减去分母里面 p 的次方。这里的计算组合数的公式为a!/(b!*(a-b)!),因此用 a 里面 p 的次方减去 b 里面 p 的次方和 (a-b) 里面 p 的次方。sum[i] = get(a,p)-get(a-b,p)-get(b,p);//是a-b不是b-a}vector<int> res;res.push_back(1);for (int i = 0; i < cnt; i ++ )//每个质数for (int j = 0; j < sum[i]; j ++ )//primes[i]的次方,即每个质数的次方res = mul(res, primes[i]);//高精度乘法for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);printf("\n");return 0;
}
满足条件的01序列:
将 01 序列置于坐标系中,起点定于原点。若 0 表示向右走,1 表示向上走,那么任何前缀中 0 的个数不少于 1的个数就转化为,路径上的任意一点,横坐标大于等于纵坐标。题目所求即为这样的合法路径数量。
答案如图,即卡特兰数。
#include <iostream>
using namespace std;typedef long long LL;const int N = 200010, mod = 1e9 + 7;int n;
int fact[N], infact[N];//fac[N]阶乘mod p的值,infac[N]阶乘的逆元mod p的值int ksm(int a, int k,int mod) //快速幂求逆元
{int res = 1;while (k) //对k进行二进制化,从低位到高位{if (k & 1) res = (LL)res * a % mod;//如果k的二进制表示的第0位为1,则乘上当前的ak >>= 1;//k右移一位a = (LL)a * a % mod;//更新a}return res;//a:4 b:3 p:9
//二进制b 11 1//res 1*4%9=4 4*7%9=1 //b 1 0 //a 4*4%9=7 16*16%9=112//因为:2^0=1,2^1=2,2^2=4,2^3=8...//根据公式:b=3,所以2^0+2^1=b,与这个指数上面的b算的一样//所以(4^2^0%9*4^2^1%9)%9=(4*7)%9=28%9=1,这个就是上面的res
}int main()
{fact[0] = infact[0] = 1;for (int i = 1; i < N; i++) {fact[i] = (LL)fact[i - 1] * i % mod;//i的阶乘就是上一个阶乘*iinfact[i] = (LL)infact[i - 1] * ksm(i, mod - 2,mod) % mod;//i的逆元阶乘就是上一个数的逆元阶乘*i的逆元}scanf("%d",&n);//由卡特兰公式:2n!/n!/n!/n+1,这里的除以n+1可以写成乘以n+1的逆元int res = (LL)fact[2 * n] * infact[n] % mod * infact[n] % mod * ksm(n + 1, mod - 2,mod) % mod;printf("%d\n",res);return 0;
}