[CF986F]Oppa Funcan Style Remastered[exgcd+同餘最短路]
阿新 • • 發佈:2018-12-26
題意
給你 \(n\) 和 \(k\) ,問能否用 \(k\) 的有 \(>1\) 的因子湊出 \(n\) 。多組資料,但保證不同的 \(k\) 不超過 50 個。
\(n\leq 10^{18}, k\leq 10^{15}\)
分析
- 記 \(k\) 的質因子數量為 \(m\) 。
如果 \(k=1\) 一定不行。
如果 \(m=1\) 直接判斷是否可以整除。
如果 \(m=2\) 就是求 \(ax+by=n\) 是否存在非負整數解。
根據 \(ax \equiv n\ (mod\ b)\) 可以得到 \(x\equiv na^{(-1)}\ (mod\ b)\)
只需要判斷最小的 \(x*a\) 是否 \(\le n\) 即可。
如果 \(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; }