• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
nannandbk
博客园    首页    新随笔    联系   管理    订阅  订阅
[数论]整数和GCD

Divisor and Gcd

1、算术基本定理:n的质因数分解唯一

一些常见结论:
1.素数无限
2.\(\lim_{n\rightarrow+\infty}n\prod\dfrac{n}{\frac{n}{\ln{n}}}\)(Π(n)表示<=n素数的个数)
————即n以下素数个数大约是 \(\frac{n}{\ln(n)}\)级别的
3.\(Pn = O(nlogn)\)级别的 (Pn表示素数)
4.\(\prod_{i = 1}^{n}\dfrac{1}{i} = O(logn)\)
5.\(\prod_{i = 1}^{n}\dfrac{1}{p} = O(loglogn)\)

2.整除的常见性质:

a|b :b能被a整除
1.若\(a|b,a|c,\)则\(a|(b±c)\)。
证明:
\(b = q1*a,c = q2*a\)
$ b±c = q1a±q2a = (q1±q2)*a;$
$ ∴a|(b±c)\( 2.\)a|c,b|c,(a,b) = 1(互质) ==> ab|c\( 3.\)a|bc,(a,b) = 1 ==> a|c\( 4.\)p|ab ==> p|a或p|b$

3.公约数和公倍数

\(a = p1^{a1}*p2^{a2}...pk^{ak}\)
\(b = p1^{b1}*p2^{b2}...pk^{bk}\)
\(d = p1^{d1}*p2^{d2}...pk^{dk}\)

\(d|a ==> d1<=a1,d2<=a2..dk<=ak\)
\(d|b ==> d1<=b1,d2<=b2..dk<=bk\)
\(di<=min(ai,bi)\)

\((a,b) = Πpi^{(min(ai,bi))}\)
\([a,b] = Πpi^{(max(ai,bi))}\)
\(∴(a,b)[a,b] = a*b\)

竞赛中遇到能被xx整除的数的特征:

  1. 能被2整除的数:个位上为2的倍数
  2. 能被4整除的数:个位和十位所组成的两位数能被4整除
  3. 能被8整除的数:百位、个位、十位所组成的三位数能被8整除(注意顺序)
  4. 能被5整除的数:个位上为5的倍数
  5. 能被25整除的数:十位和个位所组成的两位数能被25整除
  6. 能被125整除的数:百位、十位、个位所组成的数能被125整除
  7. 能被3整除的数:各个数位上的数字和能被3整除
  8. 能被9整除的数:各个数位上的数字和能被9整除
  9. 能被11整除的数:奇数位(从左往右数)上的数字和与偶数位上的数字和的差的绝对值能被11整除
  10. 能被7整除的数:把个位数字截去,从余下的数中,减去个位数的2倍,如果差是7的倍数,则原数能被7整除,如果不好判断就递归处理
  11. 能被13整除的数:把个位数字截去,从余下的数中,加上个位数的4倍,如果和是13的倍数,则原数能被13整除
  12. 能被17整除的数:把个位数字截去,从余下的数中,加上个位数的5倍,如果和是17的倍数,则原数能被17整除,或把后三位截去,剩下的数与3倍后三位差的绝对值如果能被17整除,则原数能被17整除
  13. 能被19整除的数:把个位数字截去,从余下的数中,加上个位数的2倍,如果和是19的倍数,则原数能被19整除,或把后三位截去,剩下的数与7倍后三位的差的绝对值如果能被19整除,则原数能被19整除

4.求公约数

1.欧几里得算法

d = a[1];
for(i = 2;i<=n;i++)
d = gcd(d,a[i]);
时间复杂度O(n+log(max ai))

2.另一种公约数的算法

·如果a,b都是奇数,那么\((a,b) = (a-b,b)\)
·如果a是偶数,b是奇数,那么\((a,b) = (\dfrac{a}{2},b)\)
·如果a,b都是偶数,那么\((a,b) = (\dfrac{a}{2},\dfrac{b}{2})\)

5.有关公约数和公倍数的题目

1. GCD and LCM - HDU 4497

题意:给你两个正整数\(G\)和\(L\),你可以说出有多少组解满足\(\gcd(x,y,z) = G\)并且\(lcm(x,y,z) = L\)

注意\((x,y,z)\)和\((x,z,y)\)这样是两个不同的解。

思路:从\(\gcd\)的性质考虑:

  1. 若\(\gcd(a,b) = d\),则\(\gcd(\dfrac{a}{d},\dfrac{b}{d})=1\)
  2. \(\gcd(a,b) = p_1^{min(c_1,f_1)}p_2^{min(c_2,f_2)}...p_m^{min(c_m,f_m)}\)
  3. \(lcm(a,b) = p_1^{max(c_1,f_1)}p_2^{max(c_2,f_2)}...p_m^{max(c_m,f_m)}\)

显然,若\(L\mod G!=0\)是无解的,那下面我们来看有解的情况:

\(\gcd(x,y,z) = G\)转化为\(\gcd(\dfrac{x}{G},\dfrac{y}{G},\dfrac{z}{G}) = 1\)

\(lcm(x,y,z) = L\)转化为\(lcm(\dfrac{x}{G},\dfrac{y}{G},\dfrac{z}{G}) = \dfrac{L}{G}\)

那么现在问题转化为:满足\(\dfrac{x}{G},\dfrac{y}{G},\dfrac{z}{G}\)互质的,且他们的\(lcm = \dfrac{L}{G}\)

\(\dfrac{x}{G} = p_1^{i_1}p_2^{i_2}p_3^{i_3}\)

\(\dfrac{y}{G} = p_1^{j_1}p_2^{j_2}p_3^{j_3}\)

\(\dfrac{z}{G} = p_1^{k_1}p_2^{k_2}p_3^{k_3}\)

\(\dfrac{L}{G} = p_1^{e_1}p_2^{e_2}p_3^{e_3}\)

因为要满足\(\dfrac{x}{G},\dfrac{y}{G},\dfrac{z}{G}\)互质的的条件,那么以{\(i_1,j_1,k_1\)}为例,至少有一个是\(0\)。

合法的情况是:

1){\(0,0,e_1\)} 排列方式3种

2){\(0,e_1,e_1\)} 排列方式3种

3){\(0,e_1,1~e_1-1\)} 排列方式\((e_1-1)*6\)种

综上,对于每一位有\(3+3+(e_1-1)*6 = 6*e_1\)

那么接下来问题转化为对\(\dfrac{L}{G}\)进行质因数分解,再求种类数即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		ll G,L;
		cin>>G>>L;
		if(L%G)
		{
			cout<<0<<endl;
			continue;
		}
		ll n = L/G;
		ll ans = 1;
		for(int i = 2;i<=n/i;i++)
		{
			if(n%i==0)
			{
				ll cnt = 0;
				while(n%i==0)
					n/=i,cnt++;
				ans*= 6ll*cnt;
			}
		}
		if(n>1)
			ans*=6ll;
		cout<<ans<<endl;
	}
	return 0;
}

2.【数论+完全背包】Least common multiple - HDU 3092

题意:如果给你一个数\(S\),我们把\(S\)分成一些数字,问我们这些数字组最大的\(LCM\)是多少,对结果取模?

思路:首先可以明确,分成的这几个数一定是互质的。为什么呢?假设有两个数不互质,那么他们的\(lcm\)里面势必会少乘一个他们俩的\(\gcd\),这样肯定不如都是互质的优。那么接下来,因为都是素数,那么我们可以先把素数预处理出来,然后用这些数填满\(n\),求填满\(n\)时最大的\(LCM\)。

很神奇的事情发生了,我们发现,是不是像我们有一个容量为\(n\)的背包,有\(cnt\)个物品,每个物品的体积是\(pri[i]\),可以取无限次。

对滴,是完全背包,那么接下来就可以开始写啦。

但是!这个时候我们又遇到一个问题,结果是要取模的,但是对于取模后的数我们是没有办法比大小的,中间步骤不取模又会炸,怎么办嘞?我们考虑用\(log\)来帮忙。

若\(a*b<c*d\)则有\(log^a+log^b < log^c+log^d\)

因为\(log\)在库函数里面是默认以\(10\)为底的,单调递增,那么我们的相对顺序没有变,而缩小了数字本身的大小,达到不会炸又可以比较大小的效果。

那么通过以上,问题解决。

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N =2e4+10;
bool is_pri[N];
int pri[N];
int cnt;

void init(int n)
{
	memset(is_pri, true, sizeof(is_pri));
	is_pri[1] = false;
	cnt = 0;
	for (int i = 2; i <= n-1; i++)
	{
		if (is_pri[i])
			pri[++cnt] = i;
		for (int j = 1; j <= cnt && i * pri[j] <= n-1; j++)
		{
			is_pri[i * pri[j]] = false;
			if (i % pri[j] == 0)break;            
		}
	}
}

ll n,mod;
double dp[N];
ll ans[N];
int main()
{
	init(N);
	while(cin>>n>>mod)
	{
		for(int i = 0;i<=n;i++)
			dp[i] = 0,ans[i] = 1;
		for(ll i = 1;i<=cnt&&pri[i]<=n;i++)//枚举物品
		{
			for(ll j = n;j>=pri[i];j--)//枚举体积
			{
				for(ll k = 1,now = pri[i];now<=j;now*=pri[i],k++)//枚举拿多少个(素数幂次)
				{
					//取模了无法比大小,用log
					if(now<=j&&dp[j-now]+log(now*1.0)>dp[j])
					{
						dp[j] = dp[j-now]+log(now*1.0);
						ans[j] = ans[j-now]*now%mod;
						//cout<<dp[j]<<endl;
					}
				}
			}
		}
		cout<<ans[n]%mod<<endl;
	}
	return 0;
}

6. 裴蜀定理(Bézout’s lemma)

定理内容:

是一个关于最大公约数的定理
其内容是:设\(a,b\)是不全为零的整数,则存在整数\(x,y\) , 使得\(ax+by=\gcd(a,b)\) $
(a,b)|d <==> ax+by = d $存在整数解。

推论:整数\(a,b\)互素当且仅当存在\(x\)与\(y\)使得\(ax+by=1\)

证明:对任意\(x,y,d\),\(d = ax+by\),\(d\)一定是\(\gcd(a,b)\)的整数倍。最小的\(d\)是\(\gcd(a,b)\)

P4549 【模板】裴蜀定理

思路:首先看两对数的情况\(A1X1+A2X2\),改写成裴蜀定理写法就是\(ax+by\)。根据定理知\(ax+by\)的最小非负值就是\(|\gcd(a,b)|\),这样对于\(A1X2+A2X2\)就合并成一个数\(\gcd(A1,A2)\),然后继续合并后面的。

由于\(gcd()\)会返回负数,那只需要最后一步加个绝对值就ok啦。

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

int main()
{
	int n;
	cin>>n;
	int x;
	cin>>x;
	for(int i = 2;i<=n;i++)
	{
		int y;
		cin>>y;
		x = __gcd(x,y);
	}
	cout<<abs(x)<<endl;
	return 0;
}

Pagodas - HDU 5512

思路:我们如果能得知最后能修的塔的数量,如果是奇数就先手赢,反之后手。

我们能修的塔:\(a,b,a+b,a-b,2a+b,2a-b,...\)即\(ax+by\),根据裴蜀定理知\(ax+by\)是\(\gcd(a,b)\)的整数倍。

那么数量就是\(\dfrac{n}{\gcd(a,b)}\)

7.线性丢番图方程

方程\(ax+by=c\)称为二元线性丢番图方程,其中\(a,b,c\)是已知整数,\(x,y\)是变量。

\(ax+by=c\)其实就是二维平面的一条直线,如果这条直线上有整数点那就有整数解,否则无。

如果存在一个解,那就有无数个解。

1.定理

因为\(ax+by=c\),设\(a,b\)是整数,且\(\gcd(a,b)=d\),如果\(c\)不能被\(d\)整除,那么方程无解,否则有无数解。

如果其中一个特解为\((x0,y0)\),那么通解为:\(x = x0+\dfrac{b}{d}t,y = y0-\dfrac{a}{d}t\),其中\(t\)为任意整数。

eg1.线段上格点数

题意:二维平面上,给定两个格点\(p1= (x1,y1),p2=(x2,y2)\),问线段\(p1,p2\)上除了\(p1,p2\)外还有几个格点?设\(x1<x2\)

题解:

线段\(p1p2:(y2-y1)x+(x1-x2)y = y2x1-y1x2\)

对照\(ax+by=c\),那么\(a = (y2-y1),b = (x1-x2),c = y2x1-y1x2\)

\(d = \gcd(|a|,|b|)\)

令特解为\(x1\)(这里用\(y1\)也一样),代入限制条件\(x1<x2\)

那么:\(x1<x1+\dfrac{x1-x2}{d}t<x2\)

当\(-d<t<0\)满足条件,那么有\(d-1\)个格点。

eg1.[P1516青蛙的约会](P1516 青蛙的约会 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

题意:

设青蛙 A 的出发点坐标是 \(x\),青蛙 \(B\) 的出发点坐标是 \(y\)。青蛙 \(A\) 一次能跳 \(m\) 米,青蛙 \(B\) 一次能跳 \(n\) 米,两只青蛙跳一次所花费的时间相同。纬度线总长$ L$ 米。现在要你求出它们跳了几次以后才会碰面。

题解:

思路一:青蛙相遇时,初始位置的坐标差是\(x-y\)与跳的距离\((n-m)t\)的关系为\((n-m)t+kL = x-y\)

\(设a = n-m,c = x-y,t = x',k = y',b= L\)

那么转化为我们常见的形式:\(ax'+by' = c\),其中\(x\)是我们要求的。

#incldue<bits/stdc++.h>
using namespace std;

int main()
{
    ll n,m,x,y,L;
    cin>>n>>m>>x>>y>>L;
    ll a = n-m,b = L,c = x-y;
    if(a<0)a = -a,c = -c;//细节,exgcd的参数要是正的,我们处理一下负数。
    ll d = exgcd(a,b,x,y);
    if(c%d != 0)cout<<"impossible\n";
    else cout<<(x*(c/d)%(L/d)+(L/d))%(L%d)<<endl;
    
    return 0;
}

思路二:同余

\(p1+mx \equiv p2+nx(\bmod l)\)

\((m-n)x \equiv (p2-p1)(\bmod l)\)

设\(m-n = a,l = b,p2-p1 = c\)

则有\(ax\equiv c(\bmod b)\)注意这里a可能为负数,\(\gcd\)只对非负整数有意义,所以处理一下:若\(a<0,a = -a,c = -c\)

那么\(x \equiv c\times a^{-1}(\bmod b)\),接下来我们求\(a^{-1}\)

\(ax \equiv 1(\bmod b)\)由\(exgcd\)可解出\(x\)就是\(a^{-1}\)

设\(d = exgcd(a,b,x,y)\),那么\(x = (((\frac{c}{d})\times x)\bmod(\frac{b}{d})+(\frac{b}{d}))\bmod (\frac{b}{d})\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll p1,p2,m,n,l,a,b,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x = 1,y = 0;
		return a;
	}
	ll d = exgcd(b,a%b,y,x);
	y -= (a/b)*x;
	return d;
}

int main()
{
	cin>>p1>>p2>>m>>n>>l;
	/*
		(m-n)x ≡ (p2-p1)(mod l)
		(m-n)x + kl = (p2-p1)
		a = m-n,b = l,c = p2-p1;
		ax + by = c;
	*/
	ll a = n-m,b = l,c = p1-p2;
	if(a<0)a = -a,c = -c;
	ll d = exgcd(a,b,x,y);
	//ax0+by0 = d
	//ax0+by0能被d整除,那c也要能整除
	if(c%d)cout<<"Impossible\n";
	else{
		//ax+by = c
		//ax0*(c/d)+by0*(c/d) = c
		//x0*(c/d)是一个解
		cout<<(c/d*x%(b/d)+(b/d))%(b/d)<<endl;
	}
	return 0;
}

2.关于线性丢番图方程 ax+by=n 的解的个数

令\(a,b\)是互素的正整数,\(n\)是正整数。当\(x\)和\(y\)均非负时,线性丢番图方程\(ax+by=n\)的解\((x,y)\)是非负 的。

结论1:\(n>=(a-1)(b-1)\)时,\(ax+by=n\)存在非负解。

证明:若\((x_0,y_0)\)是方程的一对特解(不一定为非负解),则\((x_0+kb,y_0-ka)\)可以取到方程的所以解(其中\(k\)为整数)

对于\(x\),存在解\(x\in[0,b-1]\),此时有:\(by=n-ax>ab-a-b-ax>=ab-a-b-a(b-1)=-b\)

所以\(y>-1\),所以此时\(ax+by=n\)存在非负解。

结论2:如果\(n=ab-a-b,\)那么\(ax+by=n\)没有非负解。

证明:

若没有非负解,即\(ax+by=ax-a-b\)

即\(a(x+1)+b(y+1)=ab\),又因为\(a,b\)互素

所以\(a|y+1,b|x+1\)

令\(ka =y+1,mb = x+1\)其中\(k,m\in Z^+\)

把\(x+1=mb,y +1=ka\)代入\(a(x+1)+b(y+1)=ab\)

得\(m+k=1\),矛盾

所以此时\(ax+by=n\)没有非负解。

反过来:

不妨假设\(a<b\)

设结果为\(n\),则\(n≡ax(\bmod b)(x\in[1,b-1])\)

可得\(n = ax+by(x\in[1,b-1])\)

当然\(y>=0\)时,\(n\)显然可以用\(a,b\)表示出来,不符合题意。

当\(y=-1\)时,满足条件的\(n\)有最大值\(n = ax-b\),显然\(x = b-1\)时候,有\(n\)的最大值\(n = (b-1)a-b=ab-a-b\)

一种感性的解法:

引用自:(https://blog.csdn.net/dglyr/article/details/108135366)

![image-20230713195242402](C:\Users\Zhou Yanan\AppData\Roaming\Typora\typora-user-images\image-20230713195242402.png)

结论3:恰好有\(\dfrac{(a-1)(b-1)}{2}\)个非负整数\(n<ab-a-b\)使得方程\(ax+by=n\)有非负解。

8. 扩展欧几里得

扩展欧几里得算法实际上就是对于\(ax+by=\gcd(a,b)\),一定有一组整数解x,y使其成立。

即扩展欧几里得是求\(ax+by=\gcd(a,b)\)的一组特解的方法。

如果要求\(ax+by=c\)的解,那么可以先利用\(exgcd\)求\(ax+by=\gcd(a,b) = d\)的一组特解,然后再两边同乘\(\dfrac{c}{d}\),\(ax_0\dfrac{c}{d}+by_0\dfrac{c}{d} = c\)

那么得到\(ax+by=c\)的一组解\((x_0',y_0')\),其中\(x_0' = x_0\dfrac{c}{d},y_0' = y_0\dfrac{c}{d}\)

通解为:\(x = x_0+\dfrac{b}{d}t,y = y_0'-\dfrac{a}{d}t\)

具体做法:

\(a \bmod b = a-(\dfrac{a}{b})*b\)
由归纳假设存在\(u',v'\)使得\(u'b+v'(a \bmod b) = d\)
即\(u'b + v'(a-(\dfrac{a}{b})*b) = d\)
\(v'a + (u'-(\dfrac{a}{b})v') = d\)
于是就得到了\((a,b)\)的解

\(eg.\)
\(ax+by = 1\)
\((b*(\dfrac{a}{b})+a \bmod b)x+by = 1;\)
商 余数
$b(\dfrac{a}{b}x+y)+(a \bmod b)x = 1; $
$ x'$ $ y'$

系数由\((a,b)->(b,a \bmod b)\)

\(①100x + 87y = 1;\)
\(②87(x+y) + 13x = 1; ———————x = -20,y = 23\)
$ x2 $ $ y2$

\(③13*(6x2+y2)+9x2 = 1;—————— x2 = 3,y2 = -20\)
$ x3 $ \(y3\)
\(④13x3 + 9y3 = 1;\)
\((9+4)x3 + 9y3 = 1;\)
\(9(x3+y3)+4x3 = 1; ————————— x3 = -2,y3 = 3\)
\(x4\) \(y4\)
\(⑤(4*2+1)x4+4y4 = 1;\)
$ 4(2x4+y4)+x4 = 1;$
设\(2x4+y4 = 0,x4 = 1\)可以找到一组解
那$x4 = 1,y4 = -2 $ ——————————————————再返回去找解

知道了特解之后,我们可以通过他求出方程的所有解
\(x = x0+k*\dfrac{b}{d}\)

\(y = y0-k*\dfrac{a}{d}\)

exgcd代码演示

//输出ax−by=gcd(a,b)的最小非负整数解(x,y)
#include<bits/stdc++.h>
using namespace std;
//为了在计算最大公约数gcd(a,b)的同时,找到x,y,使得ax + by = d
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x = 1;
		y = 0;
		return a;
	}
	/*
	int xx,yy;
	int d = exgcd(b,a%b,xx,yy);
	x = yy,yy = xx-(a/b)*yy;
	*/

	/*
	int d = exgcd(b,a%b,x,y);
	int k = y;
	y = x-(a/b)*y;
	x = k;
	*/

	int d = exgcd(b,a%b,y,x);
	y -= (a/b)*x;
	return d;
}

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int a,b,x,y;
		cin>>a>>b;
		int d = exgcd(a,b,x,y);
		// cout<<"gcd = "<<d<<endl;
		// cout<<"x = "<<x<<" y = "<<y<<endl;
		//对于ax+by = d特解是x ,y
		//通解就是x1 = x + b/(a,b)*t,y1 = y - a/(a,b)*t 

		//我们求的是ax + by = d;
		//要求ax-by = d
		//ax - b(-y) = d
		y = -y;
		//求最小正整数解,我们用exgcd求出的是最小整数解,有可能是负数的
		while(x<0||y<0)x+=b/d,y+=a/d;
		while(x>=b/d&&y>=a/d)x-=b/d,y-=a/d;
		cout<<x<<" "<<y<<endl;
	}
	return 0;
}

posted on 2023-06-17 08:58  nannandbk  阅读(91)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3