CF1011E.Border-裴蜀定理
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)
\]
所以这里复杂度的关系其实是加上去的,而不是乘。