数论+思维+组合数学+奇妙结论——cf1334E

/*
题意:给定D,用D的因子构造一张图
因子u,v之间有边:u/v是质数,边权是 u的因子数-v的因子数,
路径长度=边权之和
给定 u,v,求u,v之间最短路径的条数
怎么确定最短路径
    u->v,每步等价于u乘以一个质数或除以一个质因子,这条最短路肯定经过gcd(u,v)
    那么原问题转化为 u->gcd(u,v)的路径条数*gcd(u,v)->v的最短路径条数
怎么求u->gcd(u,v)的最短路径数量?
    u必须把多出来的那部分质因子给去掉
    结论:不管按什么方式去掉质因子,最后的路径长度是不会变的(我也不知道为什么会有这个结论..看规律是这样的)
    所以问题转化为有多少种去掉质因子的方式:就是个全排列 
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define mod 998244353
#define N 40000005

ll prime[3000005],m,F[100005],inv[100005];
bool vis[N];
ll Pow(ll a,ll b){
    ll res=1;
    while(b){
        if(b%2)res=res*a%mod;
        b>>=1;a=a*a%mod;
    }
    return res;
} 
ll D,u,v;
void init(){
    F[0]=1;
    for(int i=1;i<=100000;i++){
        F[i]=F[i-1]*i%mod;
        inv[i]=Pow(F[i],mod-2);
    }
    for(ll i=2;i*i<=D;i++)if(D%i==0){
        prime[++m]=i;
        while(D%i==0)
            D/=i;    
    }
    if(D>1)prime[++m]=D;
    sort(prime+1,prime+1+m);
    
}


ll p[1000005],mm,c[1000005];
void divide(ll x){
    mm=0;
    for(int i=1;i<=m;i++){
        if(prime[i]*prime[i]>x)break;
        if(x%prime[i]==0){
            p[++mm]=prime[i];
            c[mm]=0;
            while(x%prime[i]==0)
                ++c[mm],x/=prime[i];
        }
    }
    if(x>1){
        p[++mm]=x;
        c[mm]=1;
    }
}

ll solve(ll x){
    divide(x);
    ll res=1,sum=0;
    for(int i=1;i<=mm;i++)sum+=c[i];
    res=F[sum];
    for(int i=1;i<=mm;i++)
        res=res*inv[c[i]]%mod;
    return res;
}

int main(){
    cin>>D;
    init();
    int q;cin>>q;
    while(q--){
        scanf("%lld%lld",&u,&v);
        ll d=__gcd(u,v);
        ll ans=1;
        u/=d;v/=d;
        ans=ans*solve(u)%mod;
        ans=ans*solve(v)%mod;
        cout<<ans<<'\n';
    }
}

 

posted on 2020-04-23 20:20  zsben  阅读(181)  评论(0)    收藏  举报

导航