[Luogu P2522] [BZOJ 2301] [HAOI2011]Problem b

洛谷传送门
BZOJ传送门

题目描述

对于给出的 n n n个询问,每次求有多少个数对 ( x , y ) (x,y) (x,y),满足 a ≤ x ≤ b a≤x≤b axb c ≤ y ≤ d c≤y≤d cyd,且 g c d ( x , y ) = k gcd(x,y) = k gcd(x,y)=k g c d ( x , y ) gcd(x,y) gcd(x,y)函数为 x x x y y y的最大公约数。

输入输出格式

输入格式:

第一行一个整数 n n n,接下来 n n n行每行五个整数,分别表示 a a a b b b c c c d d d k k k

输出格式:

n n n行,每行一个整数表示满足要求的数对 ( x , y ) (x,y) (x,y)的个数

输入输出样例

输入样例#1:
2
2 5 1 5 1
1 5 1 5 2
输出样例#1:
14
3

说明

100%的数据满足: 1 ≤ n ≤ 50000 , 1 ≤ a ≤ b ≤ 50000 , 1 ≤ c ≤ d ≤ 50000 , 1 ≤ k ≤ 50000 1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000 1n500001ab500001cd500001k50000

解题分析

我们先推出这个玩意怎么求, 其他的可以通过作差得到(不妨设 n ≤ m n\le m nm):
∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = k ] = ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ [ g c d ( i , j ) = 1 ] = ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) = ∑ d = 1 ⌊ n k ⌋ μ ( d ) ⌊ ⌊ n k ⌋ d ⌋ ⌊ ⌊ m k ⌋ d ⌋ \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=k] \\ =\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}[gcd(i,j)=1] \\ =\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum_{d|gcd(i,j)}\mu(d) \\ =\sum_{d=1}^{\lfloor\frac{n}{k}\rfloor}\mu(d)\lfloor\frac{\lfloor\frac{n}{k}\rfloor}{d}\rfloor\lfloor\frac{\lfloor\frac{m}{k}\rfloor}{d}\rfloor i=1nj=1m[gcd(i,j)=k]=i=1knj=1km[gcd(i,j)=1]=i=1knj=1kmdgcd(i,j)μ(d)=d=1knμ(d)dkndkm
然后线筛 μ ( i ) \mu(i) μ(i), 下底分块即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 50050
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
int pcnt;
int miu[MX], pri[MX];
bool npr[MX];
void get_miu()
{
    R int i, j, tar;
    miu[1] = 1;
    for (i = 2; i <= 50000; ++i)
    {
        if (!npr[i]) pri[++pcnt] = i, miu[i] = -1;
        for (j = 1; j <= pcnt; ++j)
        {
            tar = i * pri[j];
            if (tar > 50000) break;
            npr[tar] = true;
            if (!(i % pri[j])) {miu[tar] = 0; break;}
            miu[tar] = -miu[i];
        }
    }
    for (R int i = 1; i <= 50000; ++i) miu[i] += miu[i - 1];
}
IN ll calc(R int up1, R int up2, R int k)
{
    if (up1 > up2) std::swap(up1, up2);
    int bd = up1 / k, bd2 = up2 / k;
    R int lef = 1, rig;
    ll ret = 0;
    W (lef <= bd)
    {
        rig = std::min(bd / (bd / lef), bd2 / (bd2 / lef));
        ret += 1ll *(miu[rig] - miu[lef - 1]) * (bd / lef) * (bd2 / lef);
        lef = rig + 1;
    }
    return ret;
}
int main()
{
    int T, a, b, c, d, k;
    in(T); get_miu();
    W (T--)
    {
        in(a), in(b), in(c), in(d), in(k);
        ll ans = calc(b, d, k) - calc(a - 1, d, k) - calc(b, c - 1, k) + calc(a - 1, c - 1, k);
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值