题目
题意:
给定一个数d,构造出一张图。点是d的所有因子,两个数x,y,若x|y并且x/y为素数,那么这两个点之间就有一条边,边权为d(x)-d(y)(d(x)为x的因子个数)。有q次访问,每个给定x,y。输出从x到y的最短路径条数。
1
≤
d
≤
1
0
15
,
1
≤
q
≤
3
⋅
1
0
5
,
1
≤
v
,
u
≤
d
1≤d≤10^{15},1≤q≤3⋅10^5,1≤v,u≤d
1≤d≤1015,1≤q≤3⋅105,1≤v,u≤d
分析:
首先我们考虑从x走到它的因子y,路径权值为d(x)-d(x1)+d(x1)-d(x2)…+d(xn)-d(y)=d(x)-d(y)。也就是说从一个点走到它的因子只与起点与终点有关。那么对于任意两个点,最短路径一定是从起点走到他们的gcd,然后走到终点。
那我们就思考从一个点走到它的因子的最短路由多少种走法呢?根据题意可以知道,每走一步值会除以一个素因子。也就是说把差值素因子分解后,直接多重集的排列数就可以了。对于素因子的分解,开始我们直接分解d,因为所有点都是他的因子,所以直接用它的素因子来分解即可。
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
ll mod = 998244353;
vector<ll> num;
ll f[105],invf[105];
ll gcd(ll a,ll b)
{
if( b == 0 ) return a;
return gcd(b,a%b);
}
ll q_pow(ll a,ll b)
{
ll res = 1;
while( b )
{
if( b & 1 ) res = ( res * a ) % mod;
a = ( a * a ) % mod;
b >>= 1;
}
return res;
}
ll inv(ll a)
{
return q_pow(a,mod-2);
}
void init(int n)
{
f[0] = 1;
invf[0] = inv(f[0]);
for (int i = 1; i <= n; i++)
{
f[i] = f[i-1] * i;
f[i] %= mod;
invf[i] = inv(f[i]);
}
}
ll slove(ll a)
{
ll res = 1;
ll temp = 0;
for (int i = 0; i < num.size(); i++)
{
ll z = 0;
while( a % num[i] == 0 )
{
a /= num[i];
z ++;
}
res *= invf[z];
res %= mod;
temp += z;
}
res *= f[temp];
res %= mod;
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
ll d,q;
cin >> d >> q;
ll a = d;
init(100);
for (ll i = 2; i * i <= d; i++)
{
if( a % i == 0 )
{
num.push_back(i);
while( a % i == 0 ) a /= i;
}
}
if( a > 1 ) num.push_back(a);
for (int i = 1; i <= q; i++)
{
ll x,y;
cin >> x >> y;
ll g = gcd(x,y);
cout << ( slove(x/g) * slove(y/g) ) % mod << '\n';
}
return 0;
}