数论基础

预备知识:

一: a*b%c=(a%c)*(b%c)%c

a^b%p=(a%p)^b%p

a/b%p=a*(b^p-2)%p (如果b和p是互质的话,可用费马小定理来写)

二:  快速幂 求a^b(思想是二分法)

a^6=a*a*a*a*a*a

a^6=(a*a*a)^2这样二分后,计算的次数大大减少了

代码如下:

long long q_pow(int a,int b)//快速幂计算 a^b%p
{
int ans=1;
if(b==0)
return 1;
while(b)
{
if(b&1)//如果b是奇数 
{
  ans*=a%p;
}
b>>=1;//相当于b/=2 
a*=a%p;

}
return ans;
}

三:Lucas定理 C(n,m)%p,其中p必须为素数,且范围在p<1e5。适用于n,m非常大的时候

可得递归式(C(n%p, m%p)*Lucas(n/p, m/p))%p

C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

具体代码:

//Lucas定理 组合数学取模 C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p 递推公式为:C(n,m)%p=(C(n%p,m%p)*Lucas(n/p,m/p))%p 
//费马小定理:假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p)。
//即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
//a*b%c=(a%c)*(b%c)%c  a^n%p=(a%p)^n%p
#include


using namespace std;
const int N=1e5;
long long fac[N];//数组计算阶乘模p 
long long n,m,p;
void init()
{
fac[0]=1;fac[1]=1;
for(int i=2;i<=p;i++)
{
   fac[i]=fac[i-1]*i%p;
}
}
long long q_pow(int a,int b)//快速幂计算 a^b
{
int ans=1;
if(b==0)
return 1;
while(b)
{
if(b&1)//如果b是奇数 
{
   ans*=a%p;
}
b>>=1;//b/=2 
a*=a%p;

}
return ans;
}
long long C(int n,int m)//计算 C(n%p,m%p)%p(这里的C是组合中的) 
{
if(m>n)
return 0;
else
return fac[n]*q_pow((fac[m]*fac[n-m]),p-2)%p;
}
long long Lucas(int n,int m)
{
if(m==0)
return 1;
else
return C(n%p,m%p)*Lucas(n/p,m/p)%p;
}
int main()
{
int t;

cin>>t;
while(t--)
{
cin>>n>>m>>p;
init();
cout<}
}

注意:当n,m很小时,可以用递归来做:C(n,m)=C(n-1,m)+C(n-1,m-1)或者之前用到的乘法逆元法

(a%c)*(b%c)%c仍然越界的话

#include
 2 #include
 3 using namespace std;
 4 
 5 __int64 Pow(__int64 a, __int64 b, __int64 c)
 6 {
 7     a %= c;
 8     b %= c;
 9     __int64 ret = 0;   //计录(a*b)%c的值。
10     while(b)
11     {
12         if(b&1)
13             ret += a, ret %= c;
14         a <<= 1; a %= c;  //a随着b中二进制位数而扩大每次扩大两倍。
15         b >>= 1;  //b来缩小两倍 去掉最后一位 由于当前最后一位我们用完了,
16     }
17     return ret;
18 }
19 
20 int main()
21 {
22     __int64 a, b, c;
23     while(~scanf("%I64d%I64d%I64d", &a, &b, &c))
24     {
25         printf("%I64d\n", Pow(a, b, c));
26     }
27     return 0;
28 }

你可能感兴趣的:(acm)