• sss


    <更新提示>

    <第一次更新>


    <正文>

    Zap

    Description

    FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a ,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。

    Input Format

    第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个 正整数,分别为a,b,d。(1<=d<=a,b<=50000)

    Output Format

    对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。

    Sample Input

    2
    4 5 2
    6 4 3
    

    Sample Output

    3
    2
    

    解析

    按照题意,可以直接设一个数论函数$$f(d)=sum_{x=1}^asum_{y=1}^b[gcd(x,y)=d]$$
    代表([1,a],[1,b])范围内最大公因数为(d)的二元组个数。

    可是这个函数好像比较难求,考虑(Möbius)反演。我们发现有函数

    [F(n)=sum_{n|d}f(d) ]

    比较好求。即(F(n))代表([1,a],[1,b])范围内最大公约数为(n)倍数的二元组个数,只需满足二元组((x,y))都是(n)倍数即可,所以有

    [F(n)=lfloor frac{a}{n} floorlfloor frac{b}{n} floor ]

    然后套(Möbius)定理,得到

    [f(d)=sum_{d|n}mu(frac{n}{d})F(n) ]

    对于求解(f(d)),有

    [f(d)=sum_{d|n}mu(frac{n}{d})F(n)=sum_{d|n}mu(frac{n}{d})lfloor frac{a}{n} floorlfloor frac{b}{n} floor ]

    (t=frac{n}{d}),由于(lfloor frac{a}{n} floorlfloor frac{b}{n} floor)(nleqmin(a,b))时有值,所以(1leq tleqfrac{min(a,b)}{d}),则

    [f(d)=sum_{t=1}^{frac{min(a,b)}{d}}mu(t)lfloor frac{a}{td} floorlfloor frac{b}{td} floor ]

    然后就可以求(f(d))的值了,由于系数(lfloor frac{a}{td} floorlfloor frac{b}{td} floor)是下取整形式的,就可以整除分块一下,对每一块相同的部分一起计算一下即可。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N=60020,Uplim=5e4;
    int a,b,k,vis[N],Prime[N],mui[N],cnt,sum[N],ans;
    inline void input(void)
    {
        scanf("%d%d%d",&a,&b,&k);
    }
    inline void sieve(void)
    {
        mui[1] = 1;
        for (int i=2;i<=Uplim;i++)
        {
            if (!vis[i])Prime[++cnt] = i , mui[i] = -1;
            for (int j=1;j<=cnt&&i*Prime[j]<=Uplim;j++)
            {
                vis[ i*Prime[j] ] = true;
                if (i%Prime[j]==0)break;
                mui[ i*Prime[j] ] = -mui[i];
            }
        }
    }
    inline void init(void)
    {
        for (int i=1;i<=Uplim;i++)
            sum[i] = sum[i-1] + mui[i];
    }
    inline void solve(void)
    {
        ans = 0 , a /= k , b /= k;
        for (int l=1,r;l<=min(a,b);l=r+1)
        {
            r = min( a/(a/l) , b/(b/l) );
            ans += (a/l) * (b/l) * (sum[r]-sum[l-1]);
        }
    }
    int main(void)
    {
        sieve();
        init();
        int T;
        scanf("%d",&T);
        while (T--)
        {
            input();
            solve();
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    <后记>

  • 相关阅读:
    Go语言 函数return值的几种情况
    VSCode 常用快捷键 Mac
    Java异常处理
    Java面向对象《三》
    C++类模板
    C++函数模板
    盛最多水的容器
    实现 Trie (前缀树)
    排序链表
    类似某团app搜索城市界面中 点击右侧城市首字母,对应城市区域置顶的功能(uniapp)
  • 原文地址:https://www.cnblogs.com/Parsnip/p/10741866.html
Copyright © 2020-2023  润新知