CF1011E.Border-裴蜀定理

yoshinow2001發表於2024-05-03

link:https://codeforces.com/contest/1011/problem/E
題意繞來繞去的不講人話,看了半天。
翻譯過來就是,給 \(a_1,\dots,a_n\) ,和一個數 \(k\),求所有的 \(d\) 滿足:存在某個 \(a_1,\dots,a_n\) 的線性組合 \(\sum a_i w_i\) 在模 \(k\) 意義下恰為 \(d\).

\(1\leq n,k\leq 10^5,1\leq a_i\leq 10^9\).


以前數學演算法可能不是很普及,E題居然還能出這種東西…取模肯定是比不取模簡單許多了(不然如果要求 \(w_i\geq 0\) 之類的還更麻煩…)

因為\(a\) 序列的理想是 \(\gcd(a_1,\dots,a_n)\)\(d\) 的所有可能取值自然是理想的模 \(k\) 下的倍數, 而這恰是理想和 \(k\) 生成的理想,即 \(\gcd(a_1,\dots,a_n,k)\) 的所有倍數…當然這題 \(k\) 很小,可以暴力列舉\(\gcd(a_1,\dots,a_n)\) 的倍數也行…

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=1e5+5;
int n,k,a[N];
int main(){
    fastio;
    cin>>n>>k;
    rep(i,1,n){
        cin>>a[i];
        a[i]%=k;
    }
    int d=k;
    rep(i,1,n)d=__gcd(a[i],d);
    vector<int> ans;
    ans.push_back(0);
    if(d){
        for(int i=d;i<k;i+=d)ans.push_back(i);
    }
    cout<<ans.size()<<endl;
    for(auto x:ans)cout<<x<<' ';
    return 0;
}

小彩蛋:時間複雜度

複雜度乍一看是 \(O(n\log \max a)\) 的?並不是,雖然單獨求 \(\gcd\) 可能是 \(O(\log \max a)\),但是這裡求的是所有數的gcd,實際的複雜度會更優:
首先計算 \(\gcd(a,b)=d\) 和計算 \(\gcd(a/d,b/d)=1\) 的代價是一樣的,因此如果 \(\gcd(a,b)=d\),求解 \(\gcd\) 的代價其實是 \(O(\min(a,b)/d)\),那麼這裡求連續的 \(\gcd\) 的複雜度其實應該是:

\[O(\sum_{i=2}^n (1+\log\frac{\min(d_{i-1},a_i)}{d_i}))\leq O(n+\sum_{i=2}^n \log \frac{d_{i-1}}{d_i})=O(n+\log\max a_i) \]

所以這裡複雜度的關係其實是加上去的,而不是乘。