题目:传送门
题意
给你一个整数D,有一个无向图,图的节点为 D 的因子,若 x % y == 0 && x / y 是一个质数,则节点 x 和 y 有一条无向边,边权为 是 x 的因子但不是 y 的因子的数的个数。有 q 次询问,每次询问输入两个节点 x、y 问节点 x 到节点 y 边权最小的路径有多少条,输出答案对 998244353 取模后的结果。
1 <= D <= 10^15; 1 <= q <= 3*10^5 ; 1 <= x,y <= D
思路
沿着图移动,就是当前点移动到当前点乘以某个质数或者除以某个质数。
从 x 到 y 的所有最短路径都经过 gcd(x, y)
答案就是 从 x 移动到 gcd(x, y) 的最短路径条数 乘以 从 y 移动到 gcd(x, y) 的最短路劲的条数。
从 x 移动到 gcd(x, y) 的最短路径,就是 x 除以 x/gcd(x,y) 的质因子,除的顺序可以随意,那就是求 x/gcd(x,y) 的质因子,然后求一个 多重集合的组合,就是 x 到 gcd(x,y) 最短路径条数。
从 y 移动到 gcd(x, y) 同理。
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF INT_MAX #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second #define lb(x) ((x) & (-(x))) using namespace std; const int N = 1e6 + 5; const LL mod = 998244353; LL ksm(LL a, LL b) { LL ans = 1LL; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } LL D; LL fac[105], ifac[105]; LL pre[105], tot; void init() { fac[0] = 1LL; rep(i, 1, 99) fac[i] = fac[i - 1] * i % mod; ifac[99] = ksm(fac[99], mod - 2); dep(i, 0, 98) ifac[i] = ifac[i + 1] * (i + 1) % mod; for(LL i = 2; i * i <= D; i++) { if(D % i == 0) { pre[++tot] = i; while(D % i == 0) D /= i; } } if(D > 1) pre[++tot] = D; } void solve() { LL x, y; scanf("%lld %lld", &x, &y); int c1 = 0, c2 = 0; LL ans = 1LL; rep(i, 1, tot) { int c = 0; while(x % pre[i] == 0) { x /= pre[i]; c++; } while(y % pre[i] == 0) { y /= pre[i]; c--; } if(c > 0) { c1 += c; ans = ans * ifac[c] % mod; } else { c2 -= c; ans = ans * ifac[-c] % mod; } } ans = ans * fac[c1] % mod * fac[c2] % mod; printf("%lld ", ans); } int main() { scanf("%lld", &D); init(); int _; scanf("%d", &_); while(_--) solve(); // solve(); return 0; }