【集训】简单数论

简单数论

P8255 [NOI Online 2022 入门组] 数学游戏

\(x\nmid z\),则一定无解。

\(x=d\times a,y=d\times b\),其中 \(\gcd(a,b)=1\)。那么有 \(\gcd(x,y)=d\)。则 \(z=x\times y\times \gcd(x,y)=d^3\times a\times b\)

我们知道 \(x\)\(z\),那么可以求出 \(\frac{z}{x}=d^2\times b\)。由于 \(\gcd(a,b)=1\),所以 \(\gcd(\frac{z}{x},x^2)=\gcd(d^2\times b,d^2\times a^2)=d^2\)。若 \(\gcd(\frac{z}{x},x^2)\) 不是完全平方数,则无解。

\(\gcd(\frac{z}{x},x^2)\) 开方即可得到 \(d\)。那么:\(\frac{\frac{z}{x}}{\sqrt{\gcd(\frac{z}{x},x^2)}}=\frac{d^2\times b}{d}=d\times b=y\)所以我们有:\(y=\frac{\frac{z}{x}}{\sqrt{\gcd(\frac{z}{x},x^2)}}\)

时间复杂度 \(\mathcal{O}(T\log x)\)

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    ll x, t;
    void Main(){
        cin >> x >> t;
        if (t % x != 0) {
            cout << -1 << endl;
            return;
        }
        t /= x;
        ll g = __gcd(x * x, t), s = sqrt(g) + 0.5;
        if (s * s != g) {
            cout << -1 << endl;
            return;
        }
        cout << t / s << endl;
    }
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}


/*
*          ▀▀▀██████▄▄▄       _______________
*          ▄▄▄▄▄  █████████▄  /                 \
*         ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Code has no BUG!  |
*      ▀▀█████▄▄ ▀██████▄██ | _________________/
*       ▀▄▄▄▄▄  ▀▀█▄▀█════█▀ |/
*            ▀▀▀▄  ▀▀███ ▀       ▄▄
*         ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌   ______________________________
*       ██▀▄▄▄██▀▄███▀ ▀▀████      ▄██  █                               \\
*    ▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███     ▌▄▄▀▀▀▀█_____________________________ //
*    ▌    ▐▀████▐███▒▒▒▒▒▐██▌
*    ▀▄▄▄▄▀   ▀▀████▒▒▒▒▄██▀
*              ▀▀█████████▀
*            ▄▄██▀██████▀█
*          ▄██▀     ▀▀▀  █
*         ▄█             ▐▌
*     ▄▄▄▄█▌              ▀█▄▄▄▄▀▀▄
*    ▌     ▐                ▀▀▄▄▄▀
*     ▀▀▄▄▀     ██
* \  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
* \- ▌           Name: Star_F              ▀ ▀
*  - ▌                            (o)          ▀
* /- ▌            Go Go Go !               ▀ ▀
* /  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
*/

P1082 [NOIP 2012 提高组] 同余方程

exgcd 模板题

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int exgcd(int a, int b, int &x, int &y){
    if (!b){
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
int main(){
    int a, b;
    cin >> a >> b;
    int x, y;
    exgcd(a, b, x, y);
    cout << (x % b + (LL)b) % b << endl; 
    return 0;
}

P1390 公约数的和

假设先求这个\(\text{ans}=\sum_{k=1}^nk*f_k\)

  • \(f_k\) 表示 \(\gcd(i,j)=k\) 的个数

  • \(g_k\) 表士 \(k|\gcd(i,j)\) 的个数,显然 \(g_k=f_k+f_{2k}+\cdots+f_{mk}\)

\(g_k=\lfloor\frac nk\rfloor^2 \ ( i,j分别由有\lfloor\frac nk\rfloor种选择)\)

所以 \(f[k]=\lfloor\frac nk\rfloor^2-(f_{2k}+f_{3k}+\cdots)\)

复杂度是 \(n\times (1+\frac12+\frac13+...+\frac1n)\)

所以总复杂的为 \(\mathcal{O}(nH(n))\)\(H(n)\) 为调和级数

因为 \(n\) 不大,这个算法常数小,所以就比算 \(\sum_{d=1}^n\phi(d)*\lfloor\frac nd\rfloor^2\) 要快一些

而本题就等于:

\[\frac{\text{ans}-\sum_{i=1}^{n}\gcd(i,i)}{2} = \frac{\text{ans}-\frac{n\times(n+1)}{2}}{2} \]

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    ll n,ans,f[2000010];
    void Main(){
        cin >> n;
        for (int i = n; i; --i){
            f[i] = n / i * (n / i);
            for (int j = i << 1; j <= n; j += i)
                f[i] -= f[j];
            ans += f[i] * i;
        }
        cout << (ans - n * (n + 1) / 2 >> 1) << endl;
    }
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    // T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}


/*
*          ▀▀▀██████▄▄▄       _______________
*          ▄▄▄▄▄  █████████▄  /                 \
*         ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Code has no BUG!  |
*      ▀▀█████▄▄ ▀██████▄██ | _________________/
*       ▀▄▄▄▄▄  ▀▀█▄▀█════█▀ |/
*            ▀▀▀▄  ▀▀███ ▀       ▄▄
*         ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌   ______________________________
*       ██▀▄▄▄██▀▄███▀ ▀▀████      ▄██  █                               \\
*    ▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███     ▌▄▄▀▀▀▀█_____________________________ //
*    ▌    ▐▀████▐███▒▒▒▒▒▐██▌
*    ▀▄▄▄▄▀   ▀▀████▒▒▒▒▄██▀
*              ▀▀█████████▀
*            ▄▄██▀██████▀█
*          ▄██▀     ▀▀▀  █
*         ▄█             ▐▌
*     ▄▄▄▄█▌              ▀█▄▄▄▄▀▀▄
*    ▌     ▐                ▀▀▄▄▄▀
*     ▀▀▄▄▀     ██
* \  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
* \- ▌           Name: Star_F              ▀ ▀
*  - ▌                            (o)          ▀
* /- ▌            Go Go Go !               ▀ ▀
* /  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
*/

P1516 青蛙的约会

求:\(x+km\equiv y+kn\pmod{l}\) ,其中 \(k\) 为所求。

化简:

\[\begin{aligned} x+km-(y+kn)&=lz,z \in Z \\ x-y+k(m-n)-lz&=0\\ k(m-n)-lz&=-(x-y) \end{aligned} \]

我们设 \(S=x-y,W=n-m\),这个式子便可写作:\(kW+lz=S\),于是用 exgcd 求出答案

让我们回到原问题。

因为 \(k_j=k_{min}+\frac{l}{gcd(W,l)} \times{t}\),所以 \(k_{min}=k_j \bmod \frac{l}{gcd(W,l)}\) 这是因为 exgcd 的性质

而因为这个 \(k\) 是建立在 \(\rm exgcd\) 得出的方程上的,方程右边是 \(gcd(W,l)\) 而不是 \(S\)。所以最后我们需要将结果 \(\times{ \frac{S}{gcd(W,l)} }\)

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }

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

    return d;
}

int main()
{
    LL a, b, m, n, L;
    cin >> a >> b >> m >> n >> L;

    LL x, y;
    LL d = exgcd(m - n, L, x, y);
    if ((b - a) % d) puts("Impossible");
    else
    {
        x *= (b - a) / d;
        LL t = abs(L / d);
        cout << (x % t + t) % t << endl;
    }

    return 0;
}

P4777 【模板】扩展中国剩余定理(EXCRT)

设有一组两两互质的正整数 \(m_1, m_2, \dots, m_n\)(即 \(\gcd(m_i, m_j) = 1\) 对任意 \(i \neq j\)),以及一组整数 \(a_1, a_2, \dots, a_n\)。中国剩余定理指出,下面的同余方程组在模 \(M = m_1 \times m_2 \times \cdots \times m_n\) 下有唯一解:

\[\begin{cases} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \vdots \\ x \equiv a_n \pmod{m_n} \end{cases} \]

  1. 解的存在性与唯一性
  • 存在性:存在解 \(x\) 满足所有同余方程。
  • 唯一性:解在模 \(M\) 下唯一,即所有解可表示为 \(x + kM\)\(k \in \mathbb{Z}\))。
  1. 解为:$$ x = \sum_{i=1}^n a_i M_i t_i \mod M $$,

证明:

  • 因为 \(M_i=M/m_i\),是除 \(m_i\) 之外所有模数的倍数,所以 \(∀k≠i,a_iM_it_i≡0(\mod m_k)\)
  • 又因为 \(M_it_i≡1(\mod m_i)\) ,所以 \(a_iM_it_i≡a_i(\mod m_i)\)
  • 综上所述,将公式带入原方程组即能够满足所有的同余式。
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 10;
int n;
int A[N], B[N];
LL exgcd(LL a, LL b, LL &x, LL &y)  // 扩展欧几里得算法, 求x, y,使得ax + by = gcd(a, b)
{
    if (!b)
    {
        x = 1; y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}
int main(){
    scanf("%d", &n);
    LL M = 1;
    for (int i = 0; i < n; i ++ ){
        scanf("%d%d", &A[i], &B[i]);
        M *= A[i];
    }
    LL res = 0;
    for (int i = 0; i < n; i ++ ){
        LL Mi = M / A[i];
        LL ti, x;
        exgcd(Mi, A[i], ti, x);
        res = (res + (__int128)B[i] * Mi * ti) % M;
        // B[i] * Mi * ti可能会超出long long范围,所以需要转化成__int128
    }
    cout << (res % M + M) % M << endl;
    return 0;
}
posted @ 2025-02-09 15:27  Star_F  阅读(34)  评论(0)    收藏  举报