Educational Codeforces Round 85 (Rated for Div. 2) E - Divisor Paths

48 篇文章 0 订阅

You are given a positive integer D

. Let's build the following graph from it:

  • each vertex is a divisor of D

(not necessarily prime, 1 and D

  • itself are also included);
  • two vertices x
  • and y (x>y) have an undirected edge between them if x is divisible by y and xy
  • is a prime;
  • the weight of an edge is the number of divisors of x
  • that are not divisors of y
    • .

    For example, here is the graph for D=12

    :

    Edge (4,12)

    has weight 3 because 12 has divisors [1,2,3,4,6,12] and 4 has divisors [1,2,4]. Thus, there are 3 divisors of 12 that are not divisors of 4 — [3,6,12]

    .

    There is no edge between 3

    and 2 because 3 is not divisible by 2. There is no edge between 12 and 3 because 123=4

    is not a prime.

    Let the length of the path between some vertices v

    and u in the graph be the total weight of edges on it. For example, path [(1,2),(2,6),(6,12),(12,4),(4,2),(2,6)] has length 1+2+2+3+1+2=11. The empty path has length 0

    .

    So the shortest path between two vertices v

    and u

    is the path that has the minimal possible length.

    Two paths a

    and b are different if there is either a different number of edges in them or there is a position i such that ai and bi

    are different edges.

    You are given q

    queries of the following form:

    • v
    u — calculate the number of the shortest paths between vertices v and u
    • .

    The answer for each query might be large so print it modulo 998244353

    .

    Input

    The first line contains a single integer D

    (1≤D≤1015

    ) — the number the graph is built from.

    The second line contains a single integer q

    (1≤q≤3⋅105

    ) — the number of queries.

    Each of the next q

    lines contains two integers v and u (1≤v,u≤D). It is guaranteed that D is divisible by both v and u (both v and u are divisors of D

    ).

    Output

    Print q

    integers — for each query output the number of the shortest paths between the two given vertices modulo 998244353.

题意:给你一个数n,它的所有因子中,能整除的连一条无向边,边的权值是权值大的那个点的因子中不不是权值小的点的因子的个数,比如6 2,6有因子1 2 3 6,2有1 2,6的因子去掉2的因子之后剩下3 6,所以权值是2,现在给你q个询问,每次询问点对(u,v)问u,v间权值相同的最短路有多少条

思路:d建边之后类似于这样的一个网格图

1.可以看的出来,假设u>v且uv相邻,当u移动到v,就是除掉了一个素因子,反之则是乘以一个素因子;

2.假设d=p1^x1*p2^x2...pn^xn;那么它的因子个数是(x1+1)*(x2+1)...*(xn+1);可以看的出来除一个素因子

减少的因子数要小于等于乘一个素因子增加的因子数(可以用4->12和4->2感受一下)

3.所以,要最短,就要u和v都不断的去除素因子,这样他们都跳到gcd(u,v)就走完了最短路,如果不是跳到gcd,那么必然有u或v要进行乘素因子的操作,这样就不是最短的了

4.而且我们可以发现去掉素因子的顺序和最后的最短路值无关

做法:把d分解,将d的素因子存储起来,对(u,v),ans=ans*C(当前素因子集合,要去除的素因子个数),v同理。因为假设我已经去除了一些素因子,他们形成了一条路,因为去掉素因子的顺序和最后的最短路值无关,我再去除素因子就相当于把这些素因子插到之前的路中,假设u=2^3*3^3;gcd(u,v)=2^1*3^1,我已经去掉了两个2,那么我继续去掉两个3就当于在_ _ _ _这四个位置选两个把两个3放进去,之前的素因子集合按之前的顺序放到剩余空位里

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1005;
const int INF = 0x3f3f3f3f;
#define ll long long
#define mod 998244353
ll inv[maxn],p[maxn],s[maxn];
ll C(ll n,ll m)
{
    return p[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
    ll t;
    //scanf("%lld",&t);
    t=1;
    while(t--)
    {
        ll d,cnt=0;
        scanf("%lld",&d);
        for(ll i=2;i*i<=d;i++)
        {
            if(d%i==0) s[++cnt]=i;
            while(d%i==0) d/=i;
        }
        if(d!=1ll) s[++cnt]=d;
        inv[0]=inv[1]=p[0]=p[1]=1ll;
        for(ll i=2;i<=102;i++)
            {
                p[i]=p[i-1]*i%mod;
                inv[i]=(mod-(mod/i))*inv[mod%i]%mod;

            }
        for(ll i=2;i<=102;i++) inv[i]=inv[i]*inv[i-1]%mod;
        /*for(ll i=1;i<=cnt;i++)
        {
            printf("s[%lld]=%lld p[%lld]=%lld inv[%lld]=%lld\n",i,s[i],i,p[i],i,inv[i]);
        }*/
        ll m;
        scanf("%lld",&m);
        while(m--)
        {
            ll ans=1,sx=0,sy=0,x,y;
            scanf("%lld %lld",&x,&y);
            for(ll i=1;i<=cnt;i++)
            {
                ll xa=0,yb=0,mx=0;
                while(x%s[i]==0)
                {
                    x/=s[i];
                    xa++;
                }
                while(y%s[i]==0)
                {
                    y/=s[i];
                    yb++;
                }
                mx=max(xa,yb);
                sx+=mx-xa;sy+=mx-yb;
                ans=ans*C(sx,mx-xa)%mod*C(sy,mx-yb)%mod;
                //printf("i=%lld ans=%lld sx=%lld sy=%lld mx=%lld xa=%lld yb=%lld C1=%lld c2=%lld\n",i,ans,sx,sy,mx,xa,yb,C(sx,mx-xa),C(sy,mx-yb));
            }
            printf("%lld\n",ans%mod);
        }

    }
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值