第一节 基础方法

Contents

最大公约数

欧几里得算法

int gcd(int x, int y) {
    return y == 0 ? x : gcd(y, x % y);
}

扩展欧几里得算法

ax+by=c 的解以及逆元(a,b,c>0,c=gcd(a,b)

// 返回 d=gcd(a,b); 和对应于等式 ax+by=d 中的 x,y;
int exgcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1; y = 0;
        return a;
    }
    int gcd = exgcd(b, a % b, x, y);
    int t = x; x = y;
    y = t - a / b * y;
    return gcd;
}

不需要求gcd时:

void exgcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1; y = 0;
    }
    else {
        exgcd(b, a % b, y, x);
        y -= a / b * x;
    }
}

一道例题:sgu106

同余方程求解

问题:

给出同余方程:cx \equiv a \pmod p,要求转化成 x \equiv a \pmod b 的形式。

// cx = a (mod p) ==> x = first (mod second)
// 事先进行 c %= p, a %= p 的操作;判断 c = 0 的特殊情况。
pair<ll, ll> get_equation(ll c, ll a, ll p) {
    ll x = 0, y = 0;
    ll gcd = exgcd(c, p, x, y), mod = p / gcd;
    if (a % gcd) {
        return make_pair(-1, -1);
    }
    x = mul(x, (a / gcd), mod);
    x = (x + mod) % mod;
    return make_pair(x, mod);
}

裴蜀定理

a,b 是不全为零的整数,则存在整数 x,y , 使得 ax+by=gcd(a,b) .

例题:CF510D Fox And Jumping

题目大意

给出 n 张卡片,分别有 l_ic_i。在一条无限长的纸带上,你可以选择花 c_i 的钱来购买卡片 i,从此以后可以向左或向右跳 l_i 个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出 -1

分析

由裴蜀定理可知,问题转化为:选取每个数有一定的代价,选取一些数使得它们 gcd=1,最小化代价。我们可以用map来转移dp,开一维滚动数组。

#include <bits/stdc++.h>
using namespace std;
const int N = 305;
int n, l[N], c[N];

int o;
map<int, int> mp[2];
int gcd(int x, int y) {
    return y == 0 ? x : gcd(y, x % y);
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &l[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
    mp[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        o ^= 1;
        for (auto x : mp[o ^ 1]) {
            int tmp = gcd(l[i], x.first);
            if(mp[o][tmp]) mp[o][tmp] = min(mp[o][tmp], x.second + c[i]);
            else mp[o][tmp] = x.second + c[i];
            if(mp[o][x.first])mp[o][x.first] = min(mp[o][x.first], x.second);
            else mp[o][x.first] = x.second;
        }
    }
    if(mp[o][1]) printf("%d\n", mp[o][1]);
    else puts("-1");
    return 0;
}

基于值域预处理的快速GCD算法

const int M = 1e6;
const int T = 1e3;

struct Fast_GCD {
    int pre[T + 2][T + 2];
    int fac[M + 2][3];
    bool isp[M + 2];
    int pri[M / 10], tot;

    void init() {
        fac[1][0] = fac[1][1] = fac[1][2] = 1;
        for (int i = 2; i <= M; ++i) {
            if (!isp[i]) {
                fac[i][0] = fac[i][1] = 1;
                fac[i][2] = i;
                pri[++tot] = i;
            }
            for (int j = 1; pri[j] * i <= M; ++j) {
                int tmp = pri[j] * i;
                isp[tmp] = true;
                fac[tmp][0] = fac[i][0] * pri[j];
                fac[tmp][1] = fac[i][1];
                fac[tmp][2] = fac[i][2];
                if (fac[tmp][0] > fac[tmp][1]) {
                    fac[tmp][0] ^= fac[tmp][1] ^= fac[tmp][0] ^= fac[tmp][1];
                }
                if (fac[tmp][1] > fac[tmp][2]) {
                    fac[tmp][1] ^= fac[tmp][2] ^= fac[tmp][1] ^= fac[tmp][2];
                }
                if (i % pri[j] == 0) {
                    break;
                }
            }
        }
        for (int i = 0; i <= T; ++i) {
            pre[0][i] = pre[i][0] = i;
        }
        for (int i = 1; i <= T; ++i) {
            for (int j = 1; j <= i; ++j) {
                pre[i][j] = pre[j][i] = pre[j][i % j];
            }
        }
    }

    int gcd(int a, int b) {
        int ans = 1;
        for (int i = 0, tmp; i < 3; ++i) {
            tmp = (fac[a][i] > T) ?
                  (b % fac[a][i] ? 1 : fac[a][i]) :
                  pre[fac[a][i]][b % fac[a][i]];
            b /= tmp;
            ans *= tmp;
        }
        return ans;
    }
}mygcd;

快速幂

普通快速幂

int ksm(int a, int b) {
    int ret = 1;
    for (; b; b >>= 1, a = 1LL * a * a % P) {
        if (b & 1) ret = 1LL * ret * a % P;
    }
    return ret;
}

O(1)光速幂

例题:2019南昌网络赛H

\frac{\sqrt{17}}{17}\left((\frac{3+\sqrt{17}}{2})^n-(\frac{3-\sqrt{17}}{2})^n\right),共10^7组询问,n\le 10^{18}

思路:
适用于同一底数要算不同指数多次快速幂。先预处理出三个底数tmp0, tmp1, tmp2。再利用BSGS的思想,预处理出tmp1、tmp2的2^{16}以内次方;再处理Great Step。这样可以省去快速幂的一个log的复杂度。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int P = 998244353;
const int sqrt17 = 473844410, inv17 = 939524097, inv2 = 499122177;
int tmp0, tmp1, tmp2;
const int MAXN = 65536;
int ans1[MAXN + 50], ans2[MAXN + 50]; // tmp1和tmp2的k次方
int pans1[MAXN + 50], pans2[MAXN + 50]; //(tmp1^65536)的k次方
int Q; ll N;

void init() {
    tmp0 = 1LL * sqrt17 * inv17 % P;
    tmp1 = 1LL * (3 + sqrt17) * inv2 % P;
    tmp2 = 1LL * (3 - sqrt17 + P) * inv2 % P;
    ans1[0] = ans2[0] = 1;
    for (int i = 1; i <= MAXN; i++) {
        ans1[i] = 1LL * ans1[i-1] * tmp1 % P;
        ans2[i] = 1LL * ans2[i-1] * tmp2 % P;
    }
    pans1[0] = pans2[0] = 1;
    int ptmp2 = ans1[MAXN], ptmp3 = ans2[MAXN];
    for(int i = 1; i <= MAXN; i++) {
        pans1[i] = 1LL * pans1[i-1] * ptmp2 % P;
        pans2[i] = 1LL * pans2[i-1] * ptmp3 % P;
    }
}
int ksm1(ll b) {
    if(b > P) b %= P - 1;
    if(b <= MAXN) return ans1[b];
    int tmp = b & 65535, ret = ans1[tmp];
    b >>= 16;
    return 1LL * pans1[b] * ret % P;
}
int ksm2(ll b) {
    if(b > P) b %= P - 1;
    if(b <= MAXN) return ans2[b];
    int tmp = b & 65535, ret = ans2[tmp];
    b >>= 16;
    return 1LL * pans2[b] * ret % P;
}

inline int query(ll x) {
    return 1LL * tmp0 * (ksm1(x) - ksm2(x) + P) % P;
}
void work() {
    ll a = 0, n = N;
    int ans = 0;
    for (int i = 1; i <= Q; i++) {
        a = query(n); n ^= (a * a); ans ^= a;
    }
    printf("%d\n", ans);
}

int main() {
    scanf("%d%lld", &Q, &N);
    init(); work();
    return 0;
}

求逆元

费马小定理

int ksm(int a, int b) {
    int ret = 1;
    for (; b; b >>= 1, a = 1LL * a * a % P) {
        if (b & 1) {
            ret = 1LL * ret * a % P;
        }
    }
    return ret;
}
int ni(int a) {
    return ksm(a, P - 2);
}

扩展欧几里得

void exgcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1; y = 0;
    }
    else {
        exgcd(b, a % b, y, x);
        y -= a / b * x;
    }
}
int ni(int a, int p) {
    int x = 0, y = 0;
    exgcd(a, p, x, y);
    return (x % p + p) % p;
}

线性预处理逆元

p\div a=x \cdots\cdots y,即 p=ax+y.

\begin{aligned}
&\therefore ax+y\equiv 0\ (mod\ p) \cr
&\therefore x+ya^{-1}\equiv 0\ (mod\ p) \cr
&\therefore a^{-1}\equiv\frac{-x}{y} \ (mod\ p)
\end{aligned}

int ni[N];
void init() {
    ni[1] = 1;
    for(int i = 2; i <= n; i++) {
        ni[i] = 1LL * (P - P / i) * ni[P % i] % P;
    }
}

评论

  1. 2 年前
    2023-1-16 12:51:46

    cialis buy online Thus, we examined human arterioles isolated from resistant hypertensive individuals by immunohistochemistry and found that PANX1 channels were expressed in vascular SMCs, where О± smooth muscle actin is also localized, and endothelial cells Figure 1G

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇