简单数学

数学相关的一些内容。

扩展欧几里得

好久没碰数论了,感觉自己的基础挂得一塌糊涂,复习一下这玩意吧。

希望求得 ax+by=cax+by=c,先同时除以 gcd 化成 ax+by=1ax+by=1 的形式。然后呢用欧几里得的思想,假设我们已经求得了 (b,a%b)(b,a\%b) 的答案,即已知:x0b+y0a%b=1x_0b+y_0a\%b=1,也就是说:

x0b+y0(abab)=1x0b+y0ay0bab=1(x0y0ab)b+y0a=1x_0b+y_0(a-b\lfloor\frac{a}{b}\rfloor)=1\\ x_0b+y_0a-y_0b\lfloor\frac{a}{b}\rfloor=1\\ (x_0-y_0\lfloor\frac{a}{b}\rfloor)b+y_0a=1

所以答案就变成了 (y0,x0aby0)(y_0,x_0-\lfloor\frac{a}{b}\rfloor y_0)。有个结论是这样求出来的特解保证了 x|x| 值最小,这就从侧面保证了 x+y|x|+|y| 最小。当然我们希望的特解应该是:x0=x×cg,y0=y×cgx_0=x\times\frac{c}{g},y_0=y\times\frac{c}{g}。另外,可以求出通解如下:

{x=x0+k×bgy=y0k×ag\begin{cases} x=x_0+k\times\frac{b}{g}\\ y=y_0-k\times\frac{a}{g} \end{cases}

板子:

struct node{int x,y,g;};
inline node exgcd(int a,int b){
	if(b==0)return (node){1,0,a};
	node an=exgcd(b,a%b);
	return (node){an.y,an.x-a/b*an.y,an.g};
}

尽管结果不会爆 int,但是在后面的运算中很有可能会用到更大的数,所以在这种题目中能开 long long 就开,总是没错的。板子

P3518:有一个结论是说,假如 xx 是密码,那么 t,gcd(x,n)t\forall t,\text{gcd}(x,n)|t 都应该是密码,因为它们都可以由 xx 硬凑出来。设密码集合为 SS,不正确的密码集合为 RR,所以考虑枚举 gcdSi\text{gcd}S_i,令为 gg,那么需要满足的是 i,gRi\forall i,g∤R_i。在此基础上,由于希望可能的密码最多,那么所有 gg 的倍数都应该利用上,也就是说此时的答案是 ng\lfloor\frac{n}{g}\rfloor。所以就是要求最小的 gg。暴力枚举并判断是不行的,可以考虑从大到小枚举 gcd(n,m)\text{gcd}(n,m) 的因数,如果当前因数在 RR 之中,那么说明这个因数的因数都是不合法的,递推地记录一下即可。有几个小的细节,一个是哈希判断的过程中可以让大于 10710^7nk\frac{n}{k} 的形式存储,这样一来就不需要 map 了。然后就是找质因数的时候一定是大于一(维生素B)。