在 O I OI OI 中,大多数情况下,善良的出题人为了避免高精度等大整数计算,常常会要求输出答案对一个数(大多是质数)取模的情况,但这衍生了一个问题:若题目中计算需用到除法而我们知道,如果 a ≡ b ( m o d c ) a \equiv b \pmod{c} a≡b(modc) 在大部分情况下 ⌊ a d ⌋ ≢ ⌊ b d ⌋ ( m o d c ) \lfloor \frac {a} {d} \rfloor \not\equiv \lfloor \frac {b} {d} \rfloor \pmod{c} ⌊da⌋≡⌊db⌋(modc) (注意一般题目会默认对一个数取模是数论(整数)意义上的取模,故这里除法为整数除法),这和等式的性质是不同的,要解决这个问题,就需要用到一个概念:乘法逆元。
我们来举个例子吧,先再实数范围举例,由小学知识可知,如果一个代数式 F F F 乘一个数 a a a 后,再乘它的倒数 1 a \frac {1} {a} a1 ,相当于没有乘 a a a (这里不考虑 0 0 0 的情况),换句话说,我们乘 1 a \frac {1} {a} a1 后,取消了代数式 F F F 乘 a a a 后值增大的影响。
不难发现这符合逆元的定义,故我们可以说一个数和其倒数互为乘法逆元。除此之外,我们还能发现一个数和其相反数互为加法逆元等等……
接下来回到代数式的例子,考虑为什么 a a a 的倒数 1 a \frac 1 a a1 能消去乘 a a a 的影响。显然,是由于乘法结合律的存在,使得我们在运算 F × a × 1 a F \times a \times \frac 1 a F×a×a1 时可以先运算 a × 1 a a \times \frac 1 a a×a1 的值,再运算它和 F F F 的乘积,而 a × 1 a = 1 a \times \frac 1 a = 1 a×a1=1 ,任何数与 1 1 1 的乘积均为其本身,从而使乘 a a a 对 F F F 值增大的影响取消。
上文在实数范围内讨论了乘法逆元,现在我们来看本文主要讨论的内容,数论模意义下的乘法逆元。
由上文的分析来看,我们可以借助“任何数与 1 1 1 的乘积均为其本身”的性质定义模意义下的乘法逆元。
故其定义为:如果说 a a a 在模 p p p 意义下的乘法逆元是 x x x ,那么 a x ≡ 1 ( m o d p ) ax \equiv 1 \pmod{p} ax≡1(modp)
举个栗子:
15 3 % 7 \frac {15} 3 \% 7 315%7
在 mod 7 意义下 3的逆元是5
所以 在 mod 7意义下 ( 15 / 3 ) % 7 = ( 15 ∗ 5 ) % 7 (15/3) \% 7 = (15 *5) \%7 (15/3)%7=(15∗5)%7
预处理阶乘和阶乘逆元法 \color{red} \text{预处理阶乘和阶乘逆元法} 预处理阶乘和阶乘逆元法
根据计算式得到
( m n ) = f a c m × i n v f n × i n v f m − n \binom{m}{n}=fac_m\times invf_n \times invf_{m-n} (nm)=facm×invfn×invfm−n
我们事先预处理阶乘及阶乘逆元,关于预处理阶乘逆元,有
i n v f i ≡ i n v f i + 1 × ( i + 1 ) ( m o d p ) invf_{i} \equiv invf_{i+1}\times (i+1) \pmod p invfi≡invfi+1×(i+1)(modp)
证明:
f a c i × i n v f i ≡ 1 ( m o d p ) fac_i \times invf_i \equiv 1 \pmod p faci×invfi≡1(modp)
f a c i − 1 × i × i n v f i ≡ 1 ( m o d p ) fac_{i-1}\times i \times invf_i \equiv 1 \pmod p faci−1×i×invfi≡1(modp)
只需要算一个就可以了
复杂度 O ( n + log p ) ∼ O ( 1 ) O(n+\log p)\sim O(1) O(n+logp)∼O(1) , p p p 为质数
共有 T T T 组询问,每组询问给出一对 n , m n,m n,m,求 C n m C_n^m Cnm,为了不让大家用到高精度,答案对 100000007 100000007 100000007 取模。
第一行一个数 T T T,接下来 T T T 行每行两个数 n , m n,m n,m,意义如题面所述。
T T T 行每行一个数表示答案。
对于 10 10% 10 的数据, 1 ≤ n , m ≤ 51 ≤ n , m ≤ 5 1≤n,m≤51≤n,m≤5 1≤n,m≤51≤n,m≤5。
对于 30 30% 30 的数据, 1 ≤ n , m ≤ 1001 ≤ n , m ≤ 100 1≤n,m≤1001≤n,m≤100 1≤n,m≤1001≤n,m≤100。
对于 60 60% 60 的数据, 1 ≤ n , m ≤ 1000 1≤n,m≤1000 1≤n,m≤1000。
对于 100 100% 100 的数据, 1 ≤ x < y ≤ 2 n 1≤x
#include
using namespace std;
typedef long long LL;
const LL p=100000007,N=1000010,maxn=1000010;
LL fac[maxn],Inv[maxn];
LL Mul(LL x,LL y) { return 1LL*x*y%p; }
LL Fast_Exponentiation(int x,int y)
{
int ans=1;
for (;y;y>>=1,x=Mul(x,x))
if (y&1) ans=Mul(ans,x);
return ans;
}
void before()
{
fac[0]=Inv[0]=1;
for (int i=1;i<=N;i++) fac[i]=Mul(fac[i-1],i); //计算阶乘
Inv[N]=Fast_Exponentiation(fac[N],p-2); //根据费马小定理,N的阶乘的p-2次方就是它的逆元
for (int i=N-1;i>=1;i--) Inv[i]=Mul(Inv[i+1],i+1); //根据上文的公式,公式中的i-1现在是i,那么公式中的i现在就是i+1了
}
void work()
{
int T;
scanf("%d",&T);
for (int i=1;i<=T;i++)
{
LL n,m;
scanf("%lld %lld",&n,&m);
if (n<m) printf("0\n");
else printf("%lld\n",Mul(fac[n],Mul(Inv[m],Inv[n-m]))); //得出m的阶乘的逆元和(n-m)的阶乘的逆元,x/m!/(n-m)!=n*Inv[m]*Inv[n-m]
}
}
void init()
{
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
}
int main()
{
init();
before();
work();
return 0;
}
给定整数 N N N 和 M M M,请找出有多少个不同的序列 a 1 × a 2 × ⋯ × a N = M a_1×a_2×⋯×a_N=M a1×a2×⋯×aN=M
你只需要输出答案 对 1 0 9 + 7 10^9+7 109+7 取余即可。
两个不同的序列指的是按顺序只要有一个元素不同即可。具体看样例解释。
两个整数 N N N 和 M M M
一个整数表示答案,注意取余
1 ≤ N ≤ 1 0 5 , 1 ≤ M ≤ 1 0 9 1≤N≤10^5,1≤M≤10^9 1≤N≤105,1≤M≤109
#include
using namespace std;
typedef long long LL;
const LL Mod=1e9+7,N=200000,maxn=200010;
LL ans=1,t=0;
LL fac[maxn],Inv[maxn],p[maxn],c[maxn];
LL Mul(LL x,LL y) { return 1LL*x*y%Mod; }
LL C(LL n,LL m)
{
return Mul(fac[n],Mul(Inv[m],Inv[n-m]));
}
LL Fast_Exponentiation(int x,int y)
{
LL ans=1;
for (;y;y>>=1,x=Mul(x,x))
if (y&1) ans=Mul(ans,x);
return ans;
}
void before()
{
fac[0]=Inv[0]=1;
for (int i=1;i<=N;i++) fac[i]=Mul(fac[i-1],i);
Inv[N]=Fast_Exponentiation(fac[N],Mod-2);
for (int i=N-1;i>=1;i--) Inv[i]=Mul(Inv[i+1],i+1);
}
void divide(int n)
{
for (int i=2;i<=sqrt(n)+1;i++)
{
if (n%i==0)
{
p[++t]=i,c[t]=0;
while ( n%i == 0) n= n/ i,c[t]++;
}
}
if (n>1)
{
p[++t]=n,c[t]=1;
}
}
int main()
{
freopen("factor.in","r",stdin);
freopen("factor.out","w",stdout);
int n,m;
scanf("%d %d",&n,&m);
divide(m); //分解质因数
before(); //预处理
for (int i=1;i<=t;i++)
{
ans=Mul(ans,C(n+c[i]-1,n-1)); //上文已经提到了
}
printf("%lld",ans);
return 0;
}
这篇文章还是极短的,很多引用了老师的课件? 仅以此写一些自己的心得与体会,此后还会有更新,但前提是蒟蒻得学更多新的知识QAQ