高斯整数学习笔记

高斯整数及其应用

高斯整数

  • 高斯整数定义:形如a+bi的复数被称为高斯整数,其中a,bZ,高斯整数的全体记作Z[i]
  • 四则运算:高斯整数的四则运算规则同复数的四则运算规则。
  • 封闭性:设α=a+bi,β=c+di是高斯整数,则α+β,αβ,αβ均是高斯整数。
  • 范数:对于高斯整数α=a+bi,记N(α)=a2+b2α的范数。显然有αα=N(α),N(αβ)=N(α)N(β)
  • 整除性:高斯整数α整除高斯整数β当且仅当γZ[i],αγ=β,记作α|β
  • 单位与相伴:若ϵ|1,则称高斯整数ϵ是单位,若ϵ是单位,则称ϵαα的一个相伴。高斯整数的单位有且仅有1,1,i,i
  • 高斯素数定义:若非零的高斯整数π不是单位,且只能够被单位和他的相伴整除,则称π为高斯素数。
  • 定理1:若高斯整数π,满足N(π)=p,其中p是有理素数,则π,π是高斯素数。
    证明

π=αβN(π)=N(α)N(β)=p,N(α)=1N(β)=1αβ,π,

最大公约数和唯一分解

  • 最大公约数:设α,βZ[i]则称α,β的最大公约数γ为满足以下两个条件的高斯整数:(i)γ|α,γ|β(ii)δ|αδ|βδ|γ
  • 带余除法:设α,βZ[i]β0,则存在高斯整数γ,ρ满足α=βγ+ρ0N(ρ)<N(β)我们称γ为商,ρ为余数。
    证明

α=a+bi,β=c+di,a+bic+di=x+yiz+yiβ|αs=[x+12],t=[y+12]x+yi=(s+f)+(t+g)if,gR|f|12|g|12γ=s+ti,ρ=αβγN(ρ)=N(β(x+yiγ))=N(β)N(f+gi)12N(β)

  • 定理2:设α,βZ[i](i)γZ[i],γα,β(ii)ν,μZ[i],μα+νβ=γ
    证明

S={N(μα+νβ)|μ,νZ[i]},γ=μ0α+ν0β,N(γ)S,γα,βδ|α,δ|βα=δο,β=δλγ=μ0οδ+ν0λδδ|γτ=μ1α+ν1βτγτ=ωγ+ξ,N(ξ)N(γ)ξμα+νβξ0N(γ)Sγ|τγ|α,γ|β

  • 欧几里得算法:令ρ0=α,ρ1=β为非零高斯整数,若连续使用高斯整数的带余除法,可以得到ρj=ρj+1γj+1+ρj+2其中N(ρj+2)<N(ρj+1),j=0,1,2,,n2并且ρn+1=0。则最后一个非零余数ρn就是α,β的最大公约数。

  • 引理1:若π是高斯素数,α,β是高斯整数,且有π|αβ,那么有π|α或者π|β
    证明

πβππβ2μ,νμβ+νπ=1μβα+νπα=απ|απαπ|β广π|α1α2αnj[1,n],π|αj

  • 唯一分解定理:设γ是非零非单位的高斯整数,则(i)γ(ii)γ=ρ1ρ2ρs=π1π2πts=t,使ρiπi,i=1,2,,s
    证明

N(γ)=21γN(γ)>2,N(γ)γγ=θμ,1<N(θ)<N(γ),1<N(μ)<N(γ),θ,μγ使(ii)γγ=ρ1ρ2ρs=π1π2πtρ1|π1π2πt1jt,ρ1|πjπ1πt使ρ1|π1ρ1π1π1使1

高斯整数与平方和

求出所有的正整数a,b满足a2+b2=n

  • 定理3:形如4k+1的有理质数p可以分解为两个有理整数的平方和。
    证明

14k+1tt21(modp)p|(ti)(t+i)p1p|tip|t+ipp(a+bi)t+i,tipα,βZ[i],N(α)>1,N(β)>1,αβ=pN(p)=p2=N(α)N(β)N(α)=N(β)=ppp=a2+b2p=(a+bi)(abi)1a+biabiabtt21(modp)gcd(t+i,p)a,bppkkp121(modp)t=kp14

  • 定理4:形如4k+3的有理素数p是高斯素数。
    证明

aa201(mod4)ppα,βZ[i],N(α)>1,N(β)>1,αβ=pN(p)=p2=N(α)N(β)N(α)=N(β)=ppp

  • 平方和

nn=2wp1a1p2a2psasq1b1q2b2qtbtpi4k+1qi4k+3nn=iw(1i)2w(c1+d1i)a1(c1d1i)a1(cs+dsi)as(csdsi)asq1b1q2b2qtbtna2+b2n(a+bi)(abi)abi=ϵ(1i)e(c1+d1i)f1(c1d1i)g1(cs+dsi)fs(csdsi)gsq1h1q2h2qthta+bi=ϵ(1+i)e(c1d1i)f1(c1+d1i)g1(csdsi)fs(cs+dsi)gsq1h1q2h2qtht(abi)(a+bi)=2e(c1+d1i)f1+g1(c1d1i)f1+g1(cs+dsi)fs+gs(csdsi)fs+gsq12h1q22h2qt2hte=w,fi+gi=ai(i=1,2,,s),2hi=bi(i=1,2,,t),a,bnn4k+3

一些例子

  • n×m的矩形放入坐标系中,使得矩形的四个角坐标均为整点,问所有不同的放置方式下矩形包含的完整1×1正方形数量之和。两个放置方式相同当且仅当他们之间可以仅通过平移重合。n,m10182022牛客暑期多校训练营7 D.The Pool

ABCDAAB=n,AD=mBa,ba2+b2=n2DB(a,b)D(c,d)s1=a,b1×1s2=c,d1×1ans=(s1+s2)×2+(ac)×(db)s1=i=0a1bxa便

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;
}
posted @   clapp  阅读(279)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示