【数论基础】- 欧拉降幂和卢卡斯定理

欧拉降幂

公式:
欧拉降幂公式
例题:
poweroj - 2366

题目描述:
题意:计算ab的c次幂 %1000000007。
输入:a,b,c。
输出:取模后的结果。
分析:
这道题就是一个板子,直接套公式,只不过有两层。直接完全套公式…很暴力。
代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
#define maxn 100005
typedef long long ll;
const int p=1e9+7;
ll qsm(ll a,ll b,ll p){
    ll res=1;
    res%=p;
    while(b){
        if(b&1) res=res*a%p;
        a=(a*a)%p;
        b>>=1;
    }
    return res;
}
ll ph(ll n){
    ll ans=n;
    for(int i=2;i*i<=n;i++){
        if(n%i==0){
            ans=ans*(i-1)/i;
            while(n%i==0) n/=i;
        }
    }
    if(ans>1) ans=ans*(n-1)/n;
    return ans;
}
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
int main(){
    ll a,b,c,d;
    while(scanf("%lld%lld%lld",&a,&b,&c)!=EOF){
        ll mi=ph(p-1);
        if(gcd(b,p-1)!=1){
            if(c>=mi) c=(c%mi)+mi;
        }
        else c=c%mi;
        d=qsm(b,c,p-1);
        printf("%lld\n",qsm(a,d%(p-1),p));
    }
    return 0;
}

卢卡斯定理:

引言:
这里我们先引入组合数的概念,即从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。

在这里插入图片描述
这里的两条性质十分重要,要牢记,来一个组合数的模板题目。
题目:SWUST OJ 82
题目描述
编写一个函数,求从n 个不同的数中取r 个数的所有选择的个数。

输入
输入n 和r 的值; 当用户输入0 0 时,程序结束。

输出
根据公式: C(n,r) = C(n, r-1) * (n - r + 1) / r 输出运算结果 输入数据不满足题意时候,输出"error!"

样例输入
复制
5 3
10 20
50 3
0 0
样例输出
10
error!
19600
说明:
他给出的这个公式也是对的,但是我感觉这道题没必要,但是其实这个公式很强,可以这样写

double c(int n, int m) {
    double res = 1;
    for(int i=1;i<=m;i++) {
        res = res*(n-i+1)/i;
    }
    return res;
}

你以为完了吗,不,还有一种求法,用矩阵来求:

double a[maxn][maxn];
void c(){
	a[0][0]=1.0; 
	for(int i=1;i<maxn;i++){
		for(int j=0;j<=i;j++){
			if(j==0||j==i) a[i][j]=1;
			else a[i][j]=a[i-1][j-1]+a[i-1][j];
		}
	}
}

这里我为什么用double呢,因为有道题就是这样卡了我很久。

代码:
这里面有我第3种组合数求法,但是对于大的数据,这种最容易超时。

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int c(int n,int m){
	if(m==0||n==m) return 1;
	if(n>m) return c(n-1,m)+c(n-1,m-1);
}
int main(){
	int n,m;
	while(~scanf("%d%d",&n,&m)){
		if(n==0&&m==0) return 0;
		if(n<m) cout<<"error!"<<endl;
		else cout<<c(n,m)<<endl;
	}
	return 0;
}

卢卡斯:
其实没什么了就是一个公式,做题的时候再套一下上面组合数的板子就ok了。
卢卡斯定理
在这里插入图片描述
言归正传:
那么现在,now,我们就来给出一道卢卡斯的模板题。
题目:
模板题:洛谷 P3807
题目背景
这是一道模板题。

题目描述
给定n,m,p(1≤n,m,p≤10 ^5)

求c(m,m+n)%p。

保证P为prime

C表示组合数。

一个测试点内包含多组数据。

输入格式
第一行一个整数T(T\le 10T≤10),表示数据组数

第二行开始共T行,每行三个数n m p,意义如上

输出格式
共T行,每行一个整数表示答案。

输入输出样例
输入 #1
2
1 2 5
2 1 5
输出 #1
3
3

思考:
卢卡斯模板题,但是在做卢卡斯的时候要回忆一下快速幂和欧拉定理,为什么呢,因为里面涉及到了除法,除法可是不能取模的哦。那么因此,我们需要转化为乘法来做。回想一下费马小定理,当p为质数的时候,我们有ap-1%p=1%p,故1/i就可以转化为ip-2%p,这样就能解决除法了呀,满足卢卡斯的条件就是p为质数所以一定成立。另外需要注意的是,对于c(n/p,m/p)其实是一个递归的过程,因此最后写出来的程序如下:

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
int f[maxn];
ll qsm(ll a,ll b,ll p){
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%p;
		b>>=1;
		a=(a*a)%p;
	}
	return res;
}
ll c(ll n,ll m,ll p){
	ll res=1;
	for(ll i=1;i<=m;i++){
		res=res*(n-i+1)*qsm(i,p-2,p)%p;
	}
	return res%p;
}
ll lucas(ll n,ll m,ll p){
	if(m==0||n==m) return 1%p;
	return lucas(n/p,m/p,p)%p*c(n%p,m%p,p)%p%p;
}
int main(){
	int t;
	ll n,m,p;
	scanf("%d",&t);
	while(t--){
		scanf("%lld%lld%lld",&n,&m,&p);
		printf("%lld\n",lucas(n+m,m,p));
	}
	return 0;
}
发布了43 篇原创文章 · 获赞 56 · 访问量 5109

猜你喜欢

转载自blog.csdn.net/tran_sient/article/details/95475507
今日推荐