数学、贪心、最大公约数与最小公倍数

如果环 $i$ 的可拨动范围 $r_i - l_i +1 \geq x$,那么 $[l_i,r_i]$ 中肯定存在一个 $x$ 的倍数。如果所有环的长度都不小于 $k$,那么显然存在让最终所有数 $\gcd$ 等于 $k$ 的解。

否则必定存在一个长度小于 $k$ 的环,枚举其中所有数字进行质因数分解,选出所有不小于 $k$ 的因数 $x$,然后 $O(n)$ 判断是否存在 $\gcd = x$ 的解。

时间复杂度 $O(n k d(V) + k \sqrt{V})$,其中 $O(n k d(V))$ 本身跑不满,加上因数大小不小于 $k$ 的限制后程序常数更小,可以在 250ms 内跑过所有测试点。

#include<bits/stdc++.h>
using namespace std;
int n,k,l[10'005],r[10'005];
bool success(int x){
    for(int i=1;i<=n;i++) if((l[i]+x-1)/x*x>r[i]) return 0;
    return 1;
}
void answer(int x){
    cout<<"Yes"<<'\n';
    for(int i=1;i<=n;i++) cout<<(l[i]+x-1)/x*x<<" \n"[i==n];
}
void force(int x){
    unordered_set<int> s;
    for(int i=l[x];i<=r[x];i++){
        int lim=__builtin_sqrt(i);
        for(int j=1;j<=lim;j++) if(i%j==0){
            if(j>=k) s.emplace(j);
            if(i/j>=k) s.emplace(i/j);
        }
    }
    for(int i:s) if(success(i)) return answer(i);
    cout<<"No"<<'\n';
}
void solution(){
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
    for(int i=1;i<=n;i++) if(r[i]-l[i]+1<k) return force(i);
    cout<<"Yes"<<'\n';
    for(int i=1;i<=n;i++) cout<<(l[i]+k-1)/k*k<<" \n"[i==n];
}
int T;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>T;
    while(T--) solution();
    return 0;
}
最后修改:2024 年 11 月 25 日
如果觉得我的文章对你有用,请随意赞赏