[题目链接]
https://codeforces.com/contest/986/problem/F
[算法]
不难发现 , 每个人都在且仅在一个简单环中 , 设这些环长的长度分别为
A1, A2 , A3 ... Alen, 那么有 :
1. A1 + A2 + A3 + .. + Alen = n
2. A1 , A2 , .. Alen为k的因子且大于或等于2
显然 , 每一个k的因数都可以分成若干个k的质因子之和 , 因此我们可以将问题转化为求是否存在 :
B1P1 + B2P2 + ... BmPm = n (其中,m为质因子的个数)
当m = 0时, 显然无解
当m = 1时 ,若n为P1的倍数 , 则有解 , 否则无解
当m = 2时 , 问题就转化为判断一个形如 :Ax + By = C , GCD(A,B) = 1的不定方程是否有非负整数解 , 我们可以用拓展欧几里得或其他算法解决 , 不再赘述
当m >= 3时 , 显然 , 最小的质因子一定小于等于k ^ (1 / 3), k最大时达到10 ^ 15 , 因此 , k ^ (1 / 3)不会超过10 ^ 5 , 我们不妨建10^5个点 ,
若(i + Pi) mod P1 = j mod P1 , 则从i向j连一条权值为Pi的边。
然后我们从0号点开始单源最短路 , 求得的最短路Dist[i]表示通过k的质因子组合出的,模P1余i的数中最小的 , 显然 , 若n >= Dist[n mod P1] , 问题有解 , 否则无解。
下面我们分析整个算法的时间复杂度 :
令Q = 50( 不同的k的个数 )
首先 , 为了减少在分解质因数上花费的时间 , 我们需要预处理质数 , 这将花费我们O( sqrt(K) ) )的时间 , 其中sqrt表示开根号
对于每个询问 , 我们需要将k分解质因数 , 我们需要花费O(Q * sqrt(K) / log(K))的时间 , 其中sqrt表示开根号
当m <= 2时我们需花费O(1) - O(logN)的时间 , 共需O(TlogN)的时间
当m >= 3时我们需要花费O(Q * k ^( 1 / 3) * logk)计算单源最短路( 需使用高效的Dijkstra + 堆算法 )
综上 , 总时间复杂度为 : O( sqrt(k) + Q * ( sqrt(k) / log(k) + k ^ (1 / 3)logk ) + TlogN) , 可以通过此题
[代码]
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll MAXN = 1e18; const ll MAXK = 1e15; const ll INF = 4e18; const int MAXV = 3.2e7 + 5; const int MAXP = 2e6 + 10; const int MAXF = 1e5 + 10; const int MAXQ = 50; const int MAXLOG = 64; ll n,k,tot,q; int f[MAXV],prime[MAXP],cnt[MAXQ + 1]; ll p[MAXQ + 1][MAXLOG],dist[MAXQ + 1][MAXF]; ll mem[MAXQ]; bool visited[MAXP]; template <typename T> inline void read(T &x) { ll f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline ll exp_mod(ll a,ll n,ll p) { ll res = 1, b = a; while (n > 0) { if (n & 1) res = res * b % p; b = b * b % p; n >>= 1; } return res; } int main() { int T; read(T); for (int i = 2; i < MAXV; i++) { if (!f[i]) prime[++tot] = f[i] = i; for (int j = 1; j <= tot; j++) { int tmp = i * prime[j]; if (tmp >= MAXV) break; f[tmp] = prime[j]; if (f[i] == prime[j]) break; } } while (T--) { read(n); read(k); if (k == 1) { printf("NO "); continue; } int pos = 0; for (int i = 1; i <= q; i++) if (mem[i] == k) pos = i; if (!pos) { pos = ++q; mem[pos] = k; cnt[pos] = 0; for (int i = 1; 1ll * prime[i] * prime[i] <= k; i++) { if (k % prime[i] == 0) { p[pos][++cnt[pos]] = prime[i]; while (k % prime[i] == 0) k /= prime[i]; } } if (k != 1) p[pos][++cnt[pos]] = k; if (cnt[pos] >= 3) { for (int i = 0; i < p[pos][1]; i++) { dist[pos][i] = INF; visited[i] = false; } dist[pos][0] = 0; static priority_queue< pair<ll,int> > q; q.push(make_pair(0,0)); while (!q.empty()) { int u = q.top().second; q.pop(); if (visited[u]) continue; visited[u] = true; for (int i = 2; i <= cnt[pos]; i++) { int to = (u + p[pos][i]) % p[pos][1]; int w = p[pos][i]; if (dist[pos][u] + w < dist[pos][to]) { dist[pos][to] = dist[pos][u] + w; q.push(make_pair(-dist[pos][to],to)); } } } } } if (cnt[pos] == 1) { if (n % p[pos][1] == 0) { printf("YES "); continue; } else { printf("NO "); continue; } } if (cnt[pos] == 2) { ll b = n % p[pos][1] * exp_mod(p[pos][2] , p[pos][1] - 2,p[pos][1]) % p[pos][1]; if (b * p[pos][2] <= n) printf("YES "); else printf("NO "); continue; } int val = n % p[pos][1]; if (n >= dist[pos][val]) printf("YES "); else printf("NO "); } return 0; }