數列

_huangweiliang發表於2024-11-29

題目描述:

給定一個長度為 \(n\) 的數列,每次操作你可以將數列中的一個數字加上一個整數 \(t\),其中 \(t\) 必須滿足 \(|t| = a\)\(|t| = b\)
你需要將所有數全部變為 \(0\),無解輸出 \(-1\),否則輸出最小運算元。

解題思路:

一眼 exgcd,但是場上不會,輸。

推一下 exgcd 吧,首先要用到裴蜀定理,如果 \(ax+by=c\) 有解,則 \(c|gcd(a,b)\),那麼我們就可以輕鬆判斷有無解了。

思考如何搞出一組解,因為 \(gcd(a,b)=gcd(b,a\%b)\), 所以要求 \(ax+by=gcd(a,b)\) 的一組解,可以先求 \(bx+(a\%b)y=gcd(b,a \%b)\) 的一組解,然後往回帶。

一直做到 \(b=0\),那麼一組可行的解就是 \(x=1,y=0\)

注意到 \(a \%b=a-\lfloor a/b\rfloor *b\),那麼我們可以化式子: \(bx+(a-\lfloor a/b\rfloor *b)y \rightarrow ay+b(x-\lfloor a/b\rfloor y)\) 則新的 \(x,y\) 也隨之而出了。

但是這樣我們只是解出了一組解,怎麼使得 \(x,y\) 的絕對值之和最小呢?

需要一個結論那就是 \(x\) 的最優解一定是取可能的最小正值或最大的非負值,比較即可,注意 \(0\) 的情況。

程式碼實現:

#include<bits/stdc++.h>
#define int long long
using namespace std;
void exgcd(int a, int b, int &x, int &y){
    if(b == 0){
        x = 1, y = 0;
        return;
    }
    exgcd(b, a % b, x, y);
    int t = x;
    x = y, y = t - (a / b) * y;
    return;
}
int gcd(int a, int b){
    return b == 0 ? a : gcd(b, a % b);
}
int n, A, B, ans;
signed main(){
    freopen("array.in", "r", stdin);
    freopen("array.out", "w", stdout);
    cin >> n >> A >> B;
    int gcd1 = gcd(A, B);
    A /= gcd1, B /= gcd1;
    if(A > B) swap(A, B);
    for(int i = 1; i <= n; i++){
        int c;
        cin >> c;
        if(c % gcd1 != 0) return cout << "-1" << endl, 0;
        c /= gcd1;
        int x = 0, y = 1;
        exgcd(A, B, x, y);
        x = c * x, y = c * y;
        int k = x / B, res = 0x3f3f3f3f3f3f3f3f;
        for(int j = -2; j <= 2; j++)
            res = min(res, abs(x - (k + j) * B) + abs(y + (k + j) * A));
        ans += res;
    }
    cout << ans << endl;
    return 0;
}

相關文章