题目:
As I am fond of making easier problems, I discovered a problem. Actually, the problem is ‘how can you make n by adding k non-negative integers?’ I think a small example will make things clear. Suppose n=4 and k=3. There are 15 solutions. They are
-
0 0 4
-
0 1 3
-
0 2 2
-
0 3 1
-
0 4 0
-
1 0 3
-
1 1 2
-
1 2 1
-
1 3 0
-
2 0 2
-
2 1 1
-
2 2 0
-
3 0 1
-
3 1 0
-
4 0 0
As I have already told you that I use to make problems easier, so, you don’t have to find the actual result. You should report the result modulo 1000,000,007.
Input
Input starts with an integer T (≤ 25000), denoting the number of test cases.
Each case contains two integer n (0 ≤ n ≤ 106) and k (1 ≤ k ≤ 106).
Output
For each case, print the case number and the result modulo 1000000007.
Sample Input
4
4 3
3 5
1000 3
1000 5
Sample Output
Case 1: 15
Case 2: 35
Case 3: 501501
Case 4: 84793457
分析与解答
b * k ≡ 1 (mod p) 是什么意思?
就是(b*k)%p=1
a mod b是什么意思?
就是a%b
这两个所有博客没人说,但是我不清楚。。
先看看什么是乘法逆元
当我们要求(a / b) mod p的值,且 a 很大,无法直接求得a / b的值时,我们就要用到乘法逆元。
满足 b * k ≡ 1 (mod p) 的 k 的值就是 b 关于 p 的乘法逆元。
我们可以通过求 b 关于 p 的乘法逆元 k,将 a 乘上 k 再模 p,即 (a * k) mod p。其结果与(a / b) mod p等价。
证:
因为 b * k ≡ 1 (mod p)
则有 b * k = p* x+1
得到 k = (p * x + 1) / b
将 k 代入(a * k) mod p
得到:
(a * (p * x + 1) / b) mod p
=((a * p * x) / b + a / b) mod p
=[((a * p * x) / b) mod p +(a / b)] mod p
=[(p * (a * x) / b) mod p +(a / b)] mod p
=(0 + (a / b)) mod p
= (a/b) mod p
1.用欧几里得扩展求逆元
ax≡1(modp)
可以等价的转化为ax−py=1 ,
检查gcd(a,p)是否等于1 ,如果是的话
套用exgcd解方程
最后解出x即可
求出来的x有可能为负数,所以结果进行变化:
x = (x * (c/gcd) % b + b) % b; 即的a的乘法逆元的解x
void Euild(ll a, ll b, ll &x, ll &y)
{if(0 == b){x = 1, y = 0;return ;}Euild(b, a%b, x, y);ll flag = x;x = y;y = flag - a/b * y;
}
2.用费马小定理
费马小定理:假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)
所以a*a^(p-2)≡1(mod p)
那a^(p-2)就是a的乘法逆元
可以利用快速幂计算
2.1那看看什么是快速幂
3^7 = 3 * 3^2 * 3^4
(7)10 = (111)2
———–4 2 1
3^15 = 3 * 3^2 * 3^4 * 3^8
(15)10 = (1111)2
———— 8 4 2 1
3^5 = 3 * 3^2 * 3^4
(5)10 = (101)2
———- 4 2 1
快速幂求x的y次方代码:
int pow_1(int x,int y){//x的y次方 int ren=x;int ans=1;while(y){if(y&1) ans*=ren;//取当前最末位的y,如果是1就继续乘ren ren*=ren;//下一位ren是当前ren的平方 1 2 4 8,这里8是x^4的平方,不是4的平方 y=y>>1;//y前进一位 }return ans;
}
3.用递推法On求解
O(n)求1~n逆元表
有时会遇到这样一种问题,
在模质数p下,求1~n逆元 n< p
这个问题有种很牛的算法,其基于以下的推导:
在求i的逆元时
p%i+[p/i]i=p
令a=p%i,b=[p/i],则有
a+bi=p
a+bi=0(mod p)
bi=-a(mod p)
i^-1=-b/a
也就是说i的逆元为:-[p/i]*(p%i)^-1
而p%i<i,那么可以从2递推到n求逆元,在求i之前p%i一定已经求出
这样就可以O(n)求出所有逆元了
(初始化 1^(-1)=1)
代码如下
inv[1] = 1;
for (int i = 2; i<MAXN; i++) inv[i]=(long long)(p-p/i)*inv[p%i]%p;
好了现在我们再看这题
代码参考:
这题一个数拆成m个数相加,拆成的数可以是0,问有这m个数一共有几种情况,看成是n个小球放到m个盒子里。
比如四个球放到三个盒子,盒子可以为空,怎么算的我实在不懂,先记住公式吧 方案数就是:C(n+m-1,m-1)
组合数公式:
约定f(a)代表a的阶乘, C(m,n) = f(m)/(f(n)*f(m-n));
在本题就是C(n+m-1,m-1) = f(n+m-1)/(f(m-1)f(n));
本题代码实现:
我们打表用数组存一个数的阶乘,
f(m)/(f(n) * f(m-n))%mod=(f(m)/f(n))%mod * (1/f(m-n))%mod
=f(m) * quick(f(m-1),mod-2) % mod quick(f(n),mod-2) % mod
a[n+k-1]quick(c,mod-2)%modquick(d,mod-2)%mod
#include <bits/stdc++.h>
#define mod 1000000007
#define ll long long
using namespace std;
ll a[2000000];
void C()//阶乘打表
{memset(a,0,sizeof(a));a[0] = a[1] = 1 ;for(int i = 2 ; i <=2000000;i++)a[i] = a[i-1]*i%mod;
}
ll quick(ll a , ll b)//快速幂取模
{ll res = 1 ;while(b){if(b&1) res = res * a %mod ;b>>=1;a = a * a %mod ;}return res ;
}
int main()
{int t ;cin>>t;C();for(int cas = 1 ; cas<=t;cas++){ll n , k ;cin>>n>>k;ll c = a[k-1];ll d = a[n];printf("Case %d: ",cas);cout<<a[n+k-1]*quick(c,mod-2)%mod*quick(d,mod-2)%mod<<endl;}return 0;
}
现在做一下推广
我们组合数求模的板子
/*
mod=1e6+3
样例输入方式:
3
4 2
5 0
6 4
*/
//这里直接求出C(M,N)%mod#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath> using namespace std;
const int INF=0x3f3f3f3f;
typedef long long LL;
const int mod=1e6+3;
const int maxn=1e6+100;
LL fac[maxn],inv[maxn];
LL Pow(LL a,LL b)
{ LL ans=1; while(b) { if(b&1) ans=(ans*a)%mod; a=(a*a)%mod; b>>=1; } return ans;
}
int main()
{ int cas=0; int n,a,b; fac[0]=1; //inv[0]=for(int i=1;i<=1000000;i++) { fac[i]=(fac[i-1]*i)%mod; //对阶乘打表 // inv[i]=Pow(fac[i],mod-2); } scanf("%d",&n); while(n--) { scanf("%d%d",&a,&b); long long c=Pow(fac[b],mod-2); long long d=Pow(fac[a-b],mod-2); LL ans=fac[a]*c%mod*d%mod; printf("Case %d: %lld\n",++cas,ans); } return 0;
}