下文讲的解都是整数解

裴蜀定理和扩展欧几里得算法

裴蜀恒等式:方程$ax+by=gcd(a,b)$存在一组特解$x=x0,y=y0$。

证明:

其实我们可以通过欧几里得算法来证明。

设$gcd(a,b)=g$。

欧几里得算法的最后一步会得到两个数$g, 0$。

然后令$x0=1,y0=0$,就得到了一组解$g \times 1 + 0 \times 0 = g$。

假设当前我们要求一组解满足$ax+by=g$,且我们已经得到了一组解满足$bx0+(a-[a/b]*b)y0=g$。

拆开来得到:$ay0+b(x0-[a/b]*y0)=g$,所以我们可以令$x1=y0, y1=x0-[a/b]*b*y0$而得到一组解$x1,y1$。

一直这么推下去就可以得到最后的答案。

证毕。

扩展欧几里得算法:求$ax+by=gcd(a,b)$的一组通解。

要想求通解求出一组特解(x0,y0)即可。

通解可表示为$x=x0+\frac{b}{g}, y=y0-\frac{a}{g}$,即两边同时减去最小公倍数。

要求特解我们发现就是刚才裴蜀恒等式的证明。

代码:

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

求解一般不定方程:

给定一个不定方程$ax+by=c$,求其通解。

容易发现如果$gcd(a,b)$不整除c则无解。

如果整除可以先算出一组方程$ax+by=gcd(a,b)$的特解$x0,y0$,然后再乘上$\frac{c}{d}$。

例题:CF510D Fox And Jumping

思路:

根据裴蜀定理,ax+by能表示出所有数当且仅当$(a,b)=1$。不难知道扩展到n项也是成立的。

所以这变成了一个背包问题,设$dp_i$代表选出的数的最大公约数为$i$的最小价值。最后的答案为$dp_1$。

我们可以通过$dp_j$来更新$dp_{gcd(j,c[i])}$。

但gcd可能很大,不能直接枚举。所以我们可以用平衡树把所有已经可能出现的gcd存下来,然后用其来更新。

注意自己也是可以单独的一个gcd的,这是初始化。

#include <bits/stdc++.h>
using namespace std;
int n, l[310], c[310];
map<int, int> mp;
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[l[1]] = c[1];
    for (int i = 2; i <= n; i++) {
        if (mp.find(l[i]) == mp.end()) mp[l[i]] = c[i];
        else mp[l[i]] = min(mp[l[i]], c[i]);
        for (map<int, int>::iterator it = mp.begin(); it != mp.end(); it++) {
            int val = it -> first;
            val = __gcd(val, l[i]);
            if (mp.find(val) == mp.end()) mp[val] = mp[it -> first] + c[i];
            else mp[val] = min(mp[val], mp[it -> first] + c[i]);
        }
    }
    if (mp.find(1) != mp.end()) printf("%d", mp[1]);
    else puts("-1");
    return 0;
}
CF510D

相关文章: