很容易发现,u->v,如果u>v,则u不断通过除以质因子缩小到v,所经路径长度时不变的。
考虑a->b的路径长度,为a有的因子,且b无的因子的个数。
观察a>b. a->x->b (a>=x>=b)与a->b 这两条路径。
前者是a有且x无的因子个数,加上x有且b无的因子个数。
显然,x无的因子,b一定也无,x有的因子,a一定也有。
则上述可以改成:a有b无的因子个数。即与a->b的路径长度相同。
有了这个性质这题就简单了。
先让u是大的数。
如果u是v的倍数直接u/v的质因子多重集排列即可。
否则先让v变成x,使得x是u的因子。再让u到v,这样路径最短。
结果是两个多重集排列的乘积。
具体看代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
const int mod = 998244353 ;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
vector<ll>p,c;
ll fac[100000+7];
ll inv[100000+7];
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
fac[0]=1;inv[0]=1;
for(int i=1;i<=100000;i++)
fac[i]=fac[i-1]*i%mod,inv[i]=qpow(fac[i],mod-2);
ll D;
int q;
cin>>D;
for(int i=2;i<=sqrt(D);i++)
{
if(D==1)break;
if(D%i==0)
{
int z=0;
while(D%i==0)
D/=i,z++;
p.pb(i),c.pb(i);
// cout<<D<<" "<<i<<endl;
}
}
if(D!=1)p.pb(D),c.pb(D);
//cout<<p.size()<<"=="<<endl;;
cin>>q;
while(q--)
{
ll U,V;
cin>>U>>V;
if(U==V)
{
cout<<1<<endl;
continue;
}
if(U>V)swap(U,V);
unordered_map<ll,ll>mp;
vector<ll>p1,p2;
for(int i=0;i<p.size();i++)//分解u的质因子,D质因子最多30个,直接枚举即可
{
if(U%p[i]==0)//先分解小的
{
int z=0;
while(U%p[i]==0)
U/=p[i],z++;
mp[p[i]]=z;
}
}
ll n1=0,n2=0;
for(int i=0;i<p.size();i++)
{
if(V%p[i]==0)
{
ll z=0;
while(V%p[i]==0)
V/=p[i],z++;
if(z>mp[p[i]])
{
p1.pb(z-mp[p[i]]);
n1+=z-mp[p[i]];
}
else
{
p2.pb(mp[p[i]]-z);
n2+=mp[p[i]]-z;
}
mp[p[i]]=0;
}
}
for(auto x:mp)
{
if(x.second==0)continue;
p2.pb(x.second);n2+=x.second;
}
ll ans=fac[n1]*fac[n2]%mod;
for(int i=0;i<p1.size();i++)ans=ans*inv[p1[i]]%mod;
//cout<<ans<<"-- "<<fac[p1[i]]<<" "<<p1[i]<<"== "<<qpow(fac[p1[i]],mod-2)<<endl;;
for(int i=0;i<p2.size();i++)ans=ans*inv[p2[i]]%mod;
cout<<ans<<endl;
}
return 0;
}