1. 程式人生 > >[CF986F]Oppa Funcan Style Remastered[exgcd+同餘最短路]

[CF986F]Oppa Funcan Style Remastered[exgcd+同餘最短路]

題意

給你 \(n\)\(k\) ,問能否用 \(k\) 的有 \(>1\) 的因子湊出 \(n\) 。多組資料,但保證不同的 \(k\) 不超過 50 個。

\(n\leq 10^{18}, k\leq 10^{15}\)

分析

  • \(k\) 的質因子數量為 \(m\)
  1. 如果 \(k=1\) 一定不行。

  2. 如果 \(m=1\) 直接判斷是否可以整除。

  3. 如果 \(m=2\) 就是求 \(ax+by=n\) 是否存在非負整數解。

    根據 \(ax \equiv n\ (mod\ b)\) 可以得到 \(x\equiv na^{(-1)}\ (mod\ b)\)

    只需要判斷最小的 \(x*a\) 是否 \(\le n\) 即可。

  4. 如果 \(m\ge 3\) 一定存在一個最小質因子 \(\le 10^5\) ,此時就可以套用同餘最短路來求解了。

    具體地,我們將最小質因子單獨拿出來,答案可以寫成 \(n\%p+kp​\) 的形式,當且僅當 \(n\%p​\) 可以用其他質因子,湊出且和 \(\le n​\) 時才能湊出 \(n​\) 。利用最短路求解這種轉移成環的問題。

  • 主要複雜度在處理質因子,可以記錄 \(5\times 10^7\) 以內的質因子加快列舉。

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
    int x = 0,f = 1;
    char ch = getchar();
    while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
    return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 1e5 + 7;
const LL inf = 1e18 + 7;
int T, qc;
bool vis[N], ans[N];
LL dis[N];
struct qs {
    LL n, k; int id;
    bool operator <(const qs &rhs) const {
        return k < rhs.k;
    }
}q[N];
struct data {
    int u;LL dis;
    bool operator <(const data &rhs) const {
        return rhs.dis < dis;
    }
};
priority_queue<data>Q;
vector<LL> pf;
void dijk(int n) {
    for(int i = 0; i < n; ++i) dis[i] = inf;
    memset(vis, 0, sizeof vis);
    dis[0] = 0;Q.push((data){ 0, 0});
    
    while(!Q.empty()) {
        int u = Q.top().u;Q.pop();
        if(vis[u]) continue;vis[u] = 1;
        for(int i = 1; i < pf.size(); ++i) {
            int v = (u + pf[i]) % n;
            if(dis[u] + pf[i] >= 0 && dis[u] + pf[i] < dis[v]) {
                dis[v] = dis[u] + pf[i];
                Q.push((data){ v, dis[v]});
            }
        }
    }
}
void exgcd(LL a, LL b, LL &x, LL &y) {
    if(!b) { x = 1, y = 0; return;}
    exgcd(b, a % b, y, x); y -= x * (a / b);
}
const int num_sz = 5e7 + 7;
int t, pc;
int pri[num_sz];
bool visp[num_sz];
void pre(int n) {
    int to;
    for(int i = 2; i <= n; ++i) {
        if(!visp[i])  pri[++pc] = i;
        for(int j = 1; (to = i * pri[j]) <= n; ++j) {
            visp[to] = 1;
            if(i % pri[j] == 0) break;
        }
    }
}
int main() {
    pre(5e7);
    T = gi();
    rep(i, 1, T) {
        LL a, b;
        scanf("%lld%lld", &a, &b);
        q[++qc] = (qs){ a, b, i};
    }
    sort(q + 1, q + 1 + qc);
    int cnt = 0;
    for(int i = 1, j = 1; i <= T; i = j + 1, j = i) {
        pf.clear();
        while(j + 1 <= T && q[j + 1].k == q[j].k) ++j;
        
        LL x = q[i].k;
        for(int k = 1, l = (int)sqrt(x); k <= pc && pri[k] <= l; ++k) if(x % pri[k] == 0){
            while(x % pri[k] == 0) x /= pri[k];
            pf.pb(pri[k]);
        }
        if(x > 1) pf.pb(x);
        if(pf.empty()) continue;
        if(pf.size() == 1) {
            for(int k = i; k <= j; ++k)
                ans[q[k].id] = q[k].n % q[k].k == 0;
            continue;
        }
        if(pf.size() == 2) {
            LL x, y, inva;
            exgcd(pf[0], pf[1], x, y);
            inva = (x + pf[1]) % pf[1];
            for(int k = i; k <= j; ++k) 
                ans[q[k].id] =pf[0] * (q[k].n % pf[1] * inva % pf[1]) <= q[k].n;
            continue;
        }
        sort(pf.begin(), pf.end());
        dijk(pf[0]);
        for(int k = i; k <= j; ++k)
            ans[q[k].id] = dis[q[k].n % pf[0]] <= q[k].n;
    }
    for(int i = 1; i <= T; ++i) puts(ans[i] ? "YES": "NO");
    return 0;
}