高斯整数学习笔记
高斯整数及其应用
高斯整数
- 高斯整数定义:形如
的复数被称为高斯整数,其中 ,高斯整数的全体记作 - 四则运算:高斯整数的四则运算规则同复数的四则运算规则。
- 封闭性:设
是高斯整数,则 均是高斯整数。 - 范数:对于高斯整数
,记 为 的范数。显然有 。 - 整除性:高斯整数
整除高斯整数 当且仅当 ,记作 - 单位与相伴:若
,则称高斯整数 是单位,若 是单位,则称 是 的一个相伴。高斯整数的单位有且仅有 。 - 高斯素数定义:若非零的高斯整数
不是单位,且只能够被单位和他的相伴整除,则称 为高斯素数。 - 定理1:若高斯整数
,满足 ,其中 是有理素数,则 是高斯素数。
证明:
最大公约数和唯一分解
- 最大公约数:设
则称 的最大公约数 为满足以下两个条件的高斯整数: - 带余除法:设
且 ,则存在高斯整数 满足 且 我们称 为商, 为余数。
证明:
- 定理2:设
, 。
证明:
-
欧几里得算法:令
为非零高斯整数,若连续使用高斯整数的带余除法,可以得到 其中 并且 。则最后一个非零余数 就是 的最大公约数。 -
引理1:若
是高斯素数, 是高斯整数,且有 ,那么有 或者 。
证明:
- 唯一分解定理:设
是非零非单位的高斯整数,则
证明:
高斯整数与平方和
求出所有的正整数
- 定理3:形如
的有理质数 可以分解为两个有理整数的平方和。
证明:
- 定理4:形如
的有理素数 是高斯素数。
证明:
- 平方和:
一些例子
- 将
的矩形放入坐标系中,使得矩形的四个角坐标均为整点,问所有不同的放置方式下矩形包含的完整 正方形数量之和。两个放置方式相同当且仅当他们之间可以仅通过平移重合。 2022牛客暑期多校训练营7 D.The Pool
code:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#define int long long
using namespace std;
const int mo = 998244353;
const int d[12] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
struct gauss {
__int128 a, b;
gauss() {}
gauss(int o, int p) {
a = o;
b = p;
}
bool operator==(const gauss& o) const { return (a == o.a) && (b == o.b); }
bool operator<(const gauss& o) const {
return (a < o.a) || ((a == o.a) && (b < o.b));
}
gauss operator-(const gauss& o) const { return gauss(a - o.a, b - o.b); }
gauss operator*(const gauss& o) const {
return gauss(a * o.a - b * o.b, a * o.b + b * o.a);
}
gauss operator%(const gauss& o) const {
int t1 = floor(((long double)a / o.b + (long double)b / o.a) /
((long double)o.a / o.b + (long double)o.b / o.a) +
0.5);
int t2 = floor(((long double)b / o.b - (long double)a / o.a) /
((long double)o.a / o.b + (long double)o.b / o.a) +
0.5);
return gauss(a, b) - o * gauss(t1, t2);
}
};
int n, m, bo[1000005], prime[200005], s0 = 0, cnt = 0, div1[1005][2],
div3[1005][3], s1 = 0, s3 = 0, s2 = 0;
gauss divid[1005], possible[2000005], st[1005][1005];
__int128 abss(__int128 o) { return (o > 0) ? o : (-o); }
int gcd(int o, int p) { return (!p) ? o : gcd(p, o % p); }
int mi(int o, int p, int mo) {
int yu = 1;
while (p) {
if (p & 1) yu = (__int128)yu * o % mo;
o = (__int128)o * o % mo;
p >>= 1;
}
return yu;
}
bool test(int o, int p) {
if (mi(o, p - 1, p) != 1) return 0;
int yu = p - 1;
while (!(yu & 1)) {
yu >>= 1;
if (mi(o, yu, p) == (p - 1)) return 1;
}
return mi(o, yu, p) == 1;
}
bool Miller_Rabin(int o) {
if (o <= 1) return 1;
for (int i = 0; i < 12; i++)
if (o == d[i]) return 1;
for (int i = 0; i < 12; i++)
if (!test(d[i], o)) return 0;
return 1;
}
int Pollard_Rho(int o) {
int x1 = 1ll * rand() * rand() * rand() * rand() % o + 1, c = rand() + 1,
y = x1;
for (int lim = 1;; lim = min(lim, lim << 1)) {
int cnt = 1;
for (int i = 1; i <= lim; i++) {
x1 = ((__int128)x1 * x1 + c) % o;
y = ((__int128)y * y + c) % o;
y = ((__int128)y * y + c) % o;
cnt = (__int128)cnt * abs(x1 - y) % o;
}
int yu = gcd(cnt, o);
if (yu > 1) return yu;
}
return o;
}
gauss gauss_mi(gauss o, int p) {
gauss yu = gauss(1, 0);
while (p) {
if (p & 1) yu = yu * o;
o = o * o;
p >>= 1;
}
return yu;
}
gauss gauss_gcd(gauss o, gauss p) {
if (p == gauss(0, 0)) return o;
return gauss_gcd(p, o % p);
}
gauss split(int o) {
int yu = 1ll * rand() * rand() * rand() * rand() % o + 1;
while (mi(yu, (o - 1) >> 1, o) != (o - 1)) {
yu = 1ll * rand() * rand() * rand() * rand() % o + 1;
}
yu = mi(yu, (o - 1) >> 2, o);
gauss t = gauss_gcd(gauss(yu, -1), gauss(o, 0));
int a1 = abss(t.a), a2 = abss(t.b);
if (a1 > a2) swap(a1, a2);
return gauss(a1, a2);
}
void insert(gauss o) { possible[++s0] = gauss(abss(o.a), abss(o.b)); }
void dfs(int o, gauss p) {
if (o > s1) {
gauss yu = p;
insert(yu);
insert(yu * gauss(0, 1));
return;
}
gauss yu = gauss(1, 0);
st[o][0] = gauss(1, 0);
for (int i = 1; i <= div1[o][1]; i++)
st[o][i] = st[o][i - 1] * gauss(divid[o].a, -divid[o].b);
for (int i = 0; i <= div1[o][1]; i++) {
dfs(o + 1, p * st[o][div1[o][1] - i] * yu);
yu = yu * divid[o];
}
}
int Mo(int o) { return (o >= mo) ? (o - mo) : o; }
int calc(__int128 a, __int128 b, __int128 c, __int128 n) {
if (a == 0) return ((b / c) % mo) * ((n + 1) % mo) % mo;
if ((a >= c) || (b >= c)) {
int s1 = ((n * (n + 1) / 2) % mo * ((a / c) % mo)) % mo;
int s2 = ((n + 1) % mo) * ((b / c) % mo) % mo;
return Mo(Mo(s1 + s2) + calc(a % c, b % c, c, n));
}
int s1 = ((n % mo) * ((a * n + b) / c) % mo -
calc(c, c - b - 1, a, (a * n + b) / c - 1)) %
mo;
return Mo(s1 + mo);
}
signed main() {
srand((unsigned)time(NULL));
for (int i = 2; i <= 100000; i++) {
if (!bo[i]) prime[++cnt] = i;
for (int j = 1; (j <= cnt) && (i * prime[j] <= 100000); j++) {
bo[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
int T;
cin >> T;
while (T--) {
scanf("%lld%lld", &n, &m);
int gg = gcd(n, m);
s1 = s2 = s3 = s0 = 0;
for (int i = 1; i <= cnt; i++)
if (gg % prime[i] == 0) {
if (prime[i] % 4 == 1) {
s1++;
div1[s1][0] = prime[i];
div1[s1][1] = 0;
while (gg % prime[i] == 0) div1[s1][1]++, gg /= prime[i];
} else if (prime[i] % 4 == 3) {
s3++;
div3[s3][0] = prime[i];
div3[s3][1] = 0;
div3[s3][2] = 1;
while (gg % prime[i] == 0) {
div3[s3][1]++;
gg /= prime[i];
div3[s3][2] *= prime[i];
}
} else
while (gg % 2 == 0) gg >>= 1, s2++;
}
while (!Miller_Rabin(gg)) {
int yu = Pollard_Rho(gg);
gg /= yu;
if (yu > gg) swap(yu, gg);
if (yu > 1) {
if (yu % 4 == 1) {
s1++;
div1[s1][0] = yu;
div1[s1][1] = 1;
while (gg % yu == 0) div1[s1][1]++, gg /= yu;
} else {
s3++;
div3[s3][0] = yu;
div3[s3][1] = div3[s3][2] = 1;
while (gg % yu == 0) {
div3[s3][1]++;
gg /= yu;
div3[s3][2] *= yu;
}
}
}
}
if (gg > 1) {
if (gg % 4 == 1) {
s1++;
div1[s1][0] = gg;
div1[s1][1] = 1;
} else {
s3++;
div3[s3][0] = gg;
div3[s3][1] = 1;
div3[s3][2] = gg;
}
}
s2 <<= 1;
for (int i = 1; i <= s1; i++) div1[i][1] <<= 1;
for (int i = 1; i <= s3; i++) div3[i][1] <<= 1;
for (int i = 1; i <= s1; i++) divid[i] = split(div1[i][0]);
gauss yu = gauss_mi(gauss(1, -1), s2);
for (int i = 1; i <= s3; i++) yu = yu * gauss(div3[i][2], 0);
dfs(1, yu);
for (int i = 1; i <= s0; i++) {
possible[i].a = abss(possible[i].a);
possible[i].b = abss(possible[i].b);
}
for (int i = 1; i <= s0; i++)
if (possible[i].a > possible[i].b)
swap(possible[i].a, possible[i].b);
sort(possible + 1, possible + s0 + 1);
possible[0] = gauss(0, 0);
int ans = 0;
gg = gcd(n, m);
for (int i = 1; i <= s0; i++) {
if (possible[i] == possible[i - 1]) continue;
if (!possible[i].a) continue;
if (!possible[i].b) continue;
int a = possible[i].a * (n / gg), b = possible[i].b * (n / gg);
int c = possible[i].b * (m / gg), d = possible[i].a * (m / gg);
int yu = calc(b, 0, a, a - 1) * 2 % mo;
yu = (yu + calc(c, 0, d, d - 1) * 2) % mo;
yu = (yu + (__int128)(c - a) * (b - d) % mo) % mo;
yu = (yu + mo) % mo;
if (possible[i].a != possible[i].b) yu = yu * 2 % mo;
ans = (ans + yu) % mo;
}
ans = (ans + (__int128)n * m % mo) % mo;
if (n != m) ans = ans * 2 % mo;
printf("%lld\n", ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下