week4小结(关于初等数论)

.
.
.
.

1.模运算

几个常用的定律:

( a + b ) mod p = ( a mod p + b mod p ) mod p

( a * b ) mod p = ( (a mod p) * (b mod p) ) mod p

c * ( a mod p ) = ( c *a ) mod ( c b ) ————————条件:(cy!=0)

.
.
.
.

2.欧几里得算法##

首先考虑一下:

对于任意两个正整数 a,b ,都有:

a=kb+r  (k,r∈N)

所以有:

r=a%b  (在这里,%指的是取余运算)

然后我们假设 c 是 a 和 b 的最大公约数,即

c=gcd(a,b)

然后,我们就能得到:

 c|a    c|b  (x|y 表示 x 能够整除 y , y能被x整除 , 也就是y/x是整数)

然后又因为上面那个式子,有:

r=a−kb

所以有:

c|r

那么我们就可以知道,既然a和b的因数也是b和(a%b)的因数,那么它们的最大公因数肯定也是相同的。

整合一下上面的式子,我们可以得到:

c=gcd(b,r)

gcd(a,b)=gcd(b,a%b)     ----------gcd(a,b)表示a和b的最大公约数

而且

gcd(a,0) =  a

.
.
.
.
辗转相除法函数代码:

int gcd(int a,int b)//就是欧几里得算法函数,即辗转相除法,求gcd(a,b)
{
	int c;
	while(b!=0)
	{
		c=a;
		a=b;
		b=c%b; 
	}
	int ans=a;
	return ans;
}

这里还有一个点:lcm ( a , b ) * gcd ( a , b ) =a * b -------------------------这里lim( a, b )表示a和b的最小公倍数

例题1
You are given an array a consisting of n integers.

Your task is to say the number of such positive integers x such that x divides each number from the array. In other words, you have to find the number of common divisors of all elements in the array.

For example, if the array a will be [2,4,6,2,10], then 1 and 2 divide each number from the array (so the answer for this test is 2).

Input
The first line of the input contains one integer n (1≤n≤4⋅105) — the number of elements in a.

The second line of the input contains n integers a1,a2,…,an (1≤ai≤1012), where ai is the i-th element of a.

Output
Print one integer — the number of such positive integers x such that x divides each number from the given array (in other words, the answer is the number of common divisors of all elements in the array).

Examples
Input
5
1 2 3 4 5
Output
1
Input
6
6 90 12 18 30 18
Output
4
题意:求数组中所有数的公约数的个数。
可以考虑先求第一个数X1与第二个数X2的gcd(X1,X2),再用这一个gcd(X1,X2)与第三个数求gcd,一路求下去。

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int maxn = 400000 + 10;
LL a[maxn],g,ans=0,n;
LL gcd(LL a,LL b)
{
	if(b==0) return a;
	else return gcd(b,a%b);
}
int main()
{
	while(cin>>n)
	{
		for(LL i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		g = a[1];
		for(LL i=2;i<=n;i++)
		{
			g = gcd( g , a[i] );
		}
		for(LL i=1;i*i<=g;i++)
		{
			if(g % i == 0)
			{
				if(i*i != g) ans += 2;
				else ans++;
			}
		}
		cout<<ans;
	}
	return 0;
}

.
.
.
.

3.扩展欧几里得算法

这里写图片描述

这里写图片描述
这里写图片描述

.
.
.
.
这里有一板子题:https://www.luogu.org/problemnew/show/P1082

题目就是求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数

可能乍一看,ax ≡ 1 (mod b)跟上面的ax+by=gcd(a,b)这一个方程不太一样啊,没事,让我们来推导一下。
.
.

首先,题目保证了b是素数,即gcd(a,b)一定是1

我们设r=a*x%b, 有a*x=b*k+r
然后ax ≡ 1 (mod b)就转换为了a*x-b*k=1

然后我们再设 y=-k,方程就转换成了 ax+by=1

a*x + b*y = gcd(a,b) = 1

就是妥妥的扩展欧几里得算法嘛!!! 递归求x的值就好啦!!!!

这里我还要提一下:我们递归求出来的x可能并不是最小正整数,还看是负数,我们这时候就需要处理一下。需要将x mod p,然后加上p(为了搞定负数),再mod p,代码就是:x = (x%p+p) % p;

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long x,y,n;//最好定全局变量
void exgcd(long long a,long long b)
{
	if(b==0) //当b=0时就是遇到了特解,可以递归回去算答案了
	{
		x=1,y=0;
		return ;
	}
	exgcd(b,a%b);
	long long k;
	k=x;
	x=y;
	y=k-(a/b)*y;
}
int main()
{
	long long a,p;
	scanf("%d%lld",&a,&p);
	exgcd(a,p);
	cout<<(x%p+p)%p;
	return 0;
}

例题1
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
Input
输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
Output
输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"
Sample Input
1 2 3 4 5
Sample Output
4

等我安装好LaTex再来手动敲公式

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#define LL long long
using namespace std;
LL x,y,k,t,c;
LL xx,yy,n,m;
LL gcd(LL a,LL b)
{
	if(b==0) return a;
	else return gcd(b,a%b);
}
void exgcd(LL a,LL b)
{
	if(!b)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	k = x;
	x = y;
	y = k - (a/b)*y;
	return ;
}
LL a,b,l,g;
int main()
{
	cin>>xx>>yy>>m>>n>>l;
	a = m-n;
	b = l;
	c = yy-xx;
	if(a<0)
	{
		a=-a;
		c=-c;
	}
	g = gcd(a,b);
	if( c%g!=0 )
	{
		cout<<"Impossible"<<endl;
	}
	else  
	{
		exgcd(a,b);
		
		x*=c/g;
		b;
		x = (x%b+b)%b;
		cout<<x;
	}
	return 0;
}

4.乘法逆元

这里写图片描述

算逆元的三个方法:

第一个方法:
这里写图片描述

inline void exgcd(LL a,LL b)//扩展欧几里得算法求乘法逆元
{
	if(b==0)
	{
		x=1,y=0;
		return ;
	}
	exgcd(b,a%b);
	LL k;
	k=x;
	x=y;
	y=k-(a/b)*y;
}

.
.
.
.

第二个方法:
这里写图片描述

int quick(int x,int p)//快速幂求乘法逆元,谨记,p是一个素数
{
	int ans=1;
	int d=p-2;
	while(d)
	{
		if(d%2==1)
		{
			ans*=x;
			ans%=p;
		}
		x*=x;
		x%=p;
		d/=2;
	}
	return ans;
}

.
.
.
.
第三个方法:
这里写图片描述

这里有一个模板题目,就是洛谷的 P3811 【模板】乘法逆元
题目传送门:https://www.luogu.org/problemnew/show/P3811

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long x,y,n,f[3000010];
void work(long long n,long long p)//线性求逆元,时间复杂度O(n) 
{
	f[1]=1;
	for(long long i=2;i<=n;i++)
	{
		f[i]=-(p/i)*f[p%i];
		f[i]=(f[i]%p+p)%p;
	}
}
int main()
{
	long long a,p,b,n,i;
	cin>>n>>p;
	work(n,p); 
	for(long long i=1;i<=n;i++)
	{
		printf("%lld\n",f[i]);//处理出   最小正整数!!
	}

	return 0;
}

5.欧拉函数

欧拉函数就是对于一个正整数n,小于n且和n互质的正整数(包括1)的个数,记作φ(n) 。

欧拉函数的通式:φ(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)*(1-1/p4)……(1-1/pn)
其中p1, p2……pn为n的所有质因数,n是不为0的整数。φ(1)=1(唯一和1互质的数就是1本身)。

(下面内容来自baidu)在这里插入图片描述
注意,欧拉函数是一个积性函数,只要M,N互质,就可以直接φ(MN)=φ(M)*φ(N),在线性求1~N的欧拉函数数值的时候很有用。
另外:若a,b互质且f(ab)=f(a)*f(b),则f(x)是积性函数
若a,b不互质且也有f(ab)=f(a)*f(b),则f(x)是完全积性函数

求单个数字的欧拉函数值:

ll Getphi(ll n)
{
    ll ans = n;
    for(int i=2; i*i <= n; ++i)
    {
        if(n%i == 0)
        {
            ans = ans/i*(i-1);
            while(n%i == 0)
                n/=i;
        }
    }
    if(n > 1) ans = ans/n*(n-1);
    return ans;
}

求1~N的欧拉函数值:
~~而且这里是可以嵌套在欧拉筛素数里面的求的

void phi
{
	phi[1] = 1; 
	for(int i=2;i<=maxn;i++)
	{
		if(pd[i] == 0)
		{
			prime[ ++tot ] = i;
			phi[i] = i-1;//i是素数直接求 
		}
		for(int j=1;j<=cnt&&i*prime[j]<=maxn;j++)
		{
			pd[ i*prime[j] ] =true;
			if(i%prime[j]==0) 
			{
				phi[ i*prime[j] ] = phi[i] * prime[j]
				//对于i%prime[j]==0,有phi[ i*prime[j] ]==phi[i] * prime[j] 
			}
			else
			{
				phi[ i*prime[j] ] = phi[i] * (prime[j]-1);
				//i和prime[j]互质,直接用积性函数性质 
			}
		}
	}
}

例题1
Do you have spent some time to think and try to solve those unsolved problem after one ACM contest?
No? Oh, you must do this when you want to become a “Big Cattle”.
Now you will find that this problem is so familiar:
The greatest common divisor GCD (a, b) of two positive integers a and b, sometimes written (a, b), is the largest divisor common to a and b. For example, (1, 2) =1, (12, 18) =6. (a, b) can be easily found by the Euclidean algorithm. Now I am considering a little more difficult problem:
Given an integer N, please count the number of the integers M (0<M<N) which satisfies (N,M)>1.
This is a simple version of problem “GCD” which you have done in a contest recently,so I name this problem “GCD Again”.If you cannot solve it still,please take a good think about your method of study.
Good Luck!
Input
Input contains multiple test cases. Each test case contains an integers N (1<N<100000000). A test case containing 0 terminates the input and this test case is not to be processed.
Output
For each integers N you should output the number of integers M in one line, and with one line of output for each line in input.
Sample Input
2
4
0
Sample Output
0
1

题意:
给一个整数N,求范围小于N中的整数中,与N的最大公约数大于1的整数的个数。
解法:
欧拉函数φ(N)是用来求小于N的整数中,与N的最大公约数为1的数的个数。那么此题的答案ans = N - φ(N) - 1。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL n,m,ans,x,g;
LL Getphi(LL x)
{
	ans = x;
	for(LL i=2;i*i<=x;i++)
	{
		if(x%i==0)
		{
			ans = ans*(i-1) / i;
			while(x%i==0) x /= i;
		}
	}
	if(x>1) ans = ans*(x-1) / x;
	return ans;
}
int main()
{
	while(cin>>n)
	{
		if(n==0) break;
		g = Getphi(n);
		cout<<n-g-1<<endl;
	}	
	return 0;
}

例题2
A lattice point (x, y) in the first quadrant (x and y are integers greater than or equal to 0), other than the origin, is visible from the origin if the line from (0, 0) to (x, y) does not pass through any other lattice point. For example, the point (4, 2) is not visible since the line from the origin passes through (2, 1). The figure below shows the points (x, y) with 0 ≤ x, y ≤ 5 with lines from the origin to the visible points.

在这里插入图片描述

Write a program which, given a value for the size, N, computes the number of visible points (x, y) with 0 ≤ x, y ≤ N.

Input
The first line of input contains a single integer C (1 ≤ C ≤ 1000) which is the number of datasets that follow.

Each dataset consists of a single line of input containing a single integer N (1 ≤ N ≤ 1000), which is the size.

Output
For each dataset, there is to be one line of output consisting of: the dataset number starting at 1, a single space, the size, a single space and the number of visible points for that size.

Sample Input
4
2
4
5
231
Sample Output
1 2 5
2 4 13
3 5 21
4 231 32549

题意:给你一个nxn的方阵,从原点去看别的点,与原点连线斜率相同的点只能看见一个,比如连上(4,2)之后就看不到(2,1);
idea:如果一个点(x,y)能被看见,那么一定不会出现在这里插入图片描述
就转化成了求互质数个数,就是欧拉函数。
注意一点,phi(0)不存在,要特判(0,1) (1,0)!!!
当然也可以直接从而开始求,后面再+3.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<iomanip>
#include<map>
#include<string>
#define LL long long
using namespace std;
const LL M = 1010;
LL pr[M],phi[M],sum=0,ans;
bool pd[M]={false};
LL n,m;
void getphi()
{
	phi[1]=1;
	for(int i=2;i<=M;i++)
	{
		if(pd[i]==false)
		{
			sum++;
			pr[sum]  = i;
			phi[i] = i-1;
		}
		for(int j=1;i*pr[j]<M&&j<=sum;j++)
		{
			pd[ i*pr[j] ] = true;
			if(i%pr[j]==0)
			{
				phi[ i*pr[j] ] = phi[i] * pr[j] ;
				break;
			}
			else 
			{
				phi[ i*pr[j] ] = phi[i] * phi[ pr[j] ];
			}
		}
	}
}
int main()
{
	cin>>n;
	LL t=0;
	getphi();
	while(n)
	{
		t++;
		ans = 0;
		scanf("%lld",&m);
		for(LL i=2;i<=m;i++)
		{
			ans += phi[i];
		}
		printf("%lld %lld %lld\n",t,m,ans*2+3);
		n--;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值