算法竞赛进阶指南——0x33【同余】


在这里插入图片描述

基本知识

费马小定理

在这里插入图片描述

欧拉定理及其推论

欧拉定理
在这里插入图片描述

推论
在这里插入图片描述

扩战欧几里得算法

【解决线性同余方程,用来求解ax+by=gcd(a,b)】

裴蜀定理【贝组定理】

  • 贝祖定理:若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by=m中的m一定是d的倍数。(特别地,如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。)
  • ax+by=c【即ax=c(mod b)】有解当且仅当gcd(a,b)|c,其特解为x=x0(c/gcd(a,b)),y=y0*(c/gcd(a,b))
int exgcd(int a, int b, int &x, int &y) //扩展欧几里得算法
{
    if (b == 0) //递归出口
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y); //递归
    int temp = x;
    x = y, y = temp - (a / b) * y; //y=x(old)-(a/b)*y(old)
    return d;                      //d=gcd(a,b)
}

线性同余方程

中国剩余定理

【解决线性同余方程组且模数两两互质,求出的是特解】
自己默认的是形如x=ai (mod bi)这样分布
在这里插入图片描述

在这里插入图片描述

int crt(int *a, int *b)//中国剩余定理
{
    int M = 1, ans = 0;
    for (int i = 1; i <= n; i++)
        M *= b[i];
    for (int i = 1; i <= n; i++)
    {
        int Mi = M / b[i];
        exgcd(Mi, b[i], x, y);//可以看作求逆元
        ans += (Mi * x * a[i]) % M;//主要根据公式Σ(ai*Mi*x)
    }
    return (ans < 0 ? ans += M : ans);
}

扩展中国剩余定理

【解决模数可不互质的线性同余方程组】
在这里插入图片描述

在这里插入图片描述

ll excrt()
{
    ll ans = a[1], lcm = b[1]; //第一个方程的解特判,m是前i个b[i]的lcm
    for (ll i = 2; i <= n; i++)
    {      //c=a[i]-x即是等式方程右边的;d=gcd(lcm,b[i])
        ll c = ((a[i] - ans) % b[i] + b[i]) % b[i], d = exgcd(lcm, b[i], x, y);//求解lcm*t+b[i]*y=gcd(lcm,b[i])
        if (c % d != 0)//有解当且仅当d|(a[i]-x)                                                 
            return -1;
        ll bg = b[i] / d;//模数bg
        x = fast_mul(x, c / d, bg);//求解lcm*t+b[i]*y=a[i]-x
        ans += x * lcm;//更新答案,这里不能用快速乘
        lcm *= bg;//lcm为前i个b[i]的lcm,所以新的lcm=(lcm*b[i])/gcd(lcm,b[i])=(lcm*b[i])/d=lcm*bg
    }
    return (ans % lcm + lcm) % lcm;//最小非负整数解
}

牛客一道小栗子Strange Way to Express Integers

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e6 + 100;
const int M = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
const int mod = 9901;
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll n, x, y;
ll a[N], b[N];
ll fast_mul(ll a, ll b, ll mo) //快速乘
{
    ll res = 0;
    for (; b; b >>= 1)
    {
        if (b & 1)
            res = (res + a) % mo;
        a = (a + a) % mo;
    }
    return res;
}
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, x, y);
    ll temp = x;
    x = y, y = temp - (a / b) * y;
    return d;
}
ll excrt() //扩展中国剩余定理
{
    ll ans = a[1], lcm = b[1]; //第一个方程的解特判,lcm是前i个b[i]的lcm
    for (ll i = 2; i <= n; i++)
    {                                                                           //c=a[i]-x即是等式方程右边的;d=gcd(lcm,b[i])
        ll c = ((a[i] - ans) % b[i] + b[i]) % b[i], d = exgcd(lcm, b[i], x, y); //求解lcm*t+b[i]*y=gcd(lcm,b[i])
        if (c % d != 0)                                                         //有解当且仅当d|(a[i]-x)
            return -1;
        ll bg = b[i] / d;
        x = fast_mul(x, c / d, bg); //求解lcm*t+b[i]*y=a[i]-x
        ans += x * lcm;             //更新答案,这里不能用快速乘
        lcm *= bg;                  //lcm为前i个b[i]的lcm,所以新的lcm=(lcm*b[i])/gcd(lcm,b[i])=(lcm*b[i])/d=lcm*bg
    }
    return (ans % lcm + lcm) % lcm;
}
int main()
{
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++)
        scanf("%lld%lld", &b[i], &a[i]);
    printf("%lld", excrt());
    return 0;
}

高次同余方程

大步小步算法

用来解决高次同余方程【前提:a,p互质】

ll bsgs(ll a, ll b, ll p) //大步小步算法【求解a^x=b( mod p),关键思路是将x写成x=i*t-j】
{
    map<ll, ll> ha;
    ha.clear(), b %= p;        //数据的预处理
    ll t = (ll)sqrt(p) + 1;    //√p向上取整
    for (ll j = 0; j < t; j++) //将b*a^j放进hash表
    {
        ll val = b * fast_pow(a, j, p) % p; //别忘记%p
        ha[val] = j;
    }
    a = fast_pow(a, t, p);
    if (a == 0)
        return (b == 0 ? 1 : -1);//很重要
    for (ll i = 0; i <= t; i++)
    {
        ll val = fast_pow(a, i, p);
        ll j = (ha.find(val) == ha.end() ? -1 : ha[val]);
        if (j >= 0 && i * t - j >= 0)
            return i * t - j;
    }
    return -1;
}

洛谷一道小栗子计算器

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e6 + 100;
const int M = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
const int mod = 9901;
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t, k, x, y, z, p, ans;
ll fast_pow(ll a, ll b, ll mo)
{
    ll res = 1;
    for (; b; b >>= 1)
    {
        if (b & 1)
            res = res * a % mo;
        a = a * a % mo;
    }
    return res % mo;
}
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, x, y);
    ll temp = x;
    x = y, y = temp - (a / b) * y;
    return d;
}
ll bsgs(ll a, ll b, ll p) //大步小步算法
{
    map<ll, ll> ha;
    ha.clear(), b %= p;        //数据的预处理
    ll t = (ll)sqrt(p) + 1;    //√p向上取整
    for (ll j = 0; j < t; j++) //将b*a^j放进hash表
    {
        ll val = b * fast_pow(a, j, p) % p; //别忘记%p
        ha[val] = j;
    }
    a = fast_pow(a, t, p);
    if (a == 0)
        return (b == 0 ? 1 : -1); //很重要
    for (ll i = 0; i <= t; i++)
    {
        ll val = fast_pow(a, i, p);
        ll j = (ha.find(val) == ha.end() ? -1 : ha[val]);
        if (j >= 0 && i * t - j >= 0)
            return i * t - j;
    }
    return -1;
}
int main()
{
    scanf("%lld%lld", &t, &k);
    while (t--)
    {
        ans = 0;
        scanf("%lld%lld%lld", &y, &z, &p);
        if (k == 1)
            ans = fast_pow(y, z, p);
        else if (k == 2)
        {
            ll d = exgcd(y, p, x, y);
            if (z % d) //先判断是否有解【d|c】
                ans = -1;
            else
            {
                ans = x * z / d;
                ans = (ans % p + p) % p; //最小非负整数
            }
        }
        else if (k == 3)
            ans = bsgs(y, z, p);
        if (ans == -1)
            puts("Orz, I cannot find x!");
        else
            printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WTcrazy _

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值