Luogu3455[POI2007] ZAP-Queries

原题链接:https://www.luogu.org/problemnew/show/P3455

ZAP-Queries

题目描述

Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Security Agency). He has alreadyfound out that whilst deciphering a message he will have to answer multiple queries of the form”for givenintegers aa , bb and dd , find the number of integer pairs (x,y) ( x , y ) satisfying the following conditions:

1xa 1 ≤ x ≤ a , 1yb 1 ≤ y ≤ b , gcd(x,y)=d g c d ( x , y ) = d , where gcd(x,y) g c d ( x , y ) is the greatest common divisor of xx and yy “.

Byteasar would like to automate his work, so he has asked for your help.

TaskWrite a programme which:

reads from the standard input a list of queries, which the Byteasar has to give answer to, calculates answers to the queries, writes the outcome to the standard output.

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

输入输出格式
输入格式:

The first line of the standard input contains one integer n(1n50 000) n ( 1 ≤ n ≤ 50   000 ) ,denoting the number of queries.

The following nn lines contain three integers each: a a , b and d d (1da,b50 000 ), separated by single spaces.

Each triplet denotes a single query.

输出格式:

Your programme should write nn lines to the standard output. The ii ‘th line should contain a single integer: theanswer to the ii ‘th query from the standard input.

输入输出样例
输入样例#1:

2
4 5 2
6 4 3

输出样例#1:

3
2

题解

由题,我们可以得到最直观的式子:

ans=i=1aj=1b[gcd(i,j)=d] a n s = ∑ i = 1 a ∑ j = 1 b [ g c d ( i , j ) = d ]

那么,我们可以考虑提出式子里的 d d ,相当于缩小了我们枚举的范围,答案不变:
ans=i=1adj=1bd[gcd(i,j)=1]

n=ad,m=bd n = ⌊ a d ⌋ , m = ⌊ b d ⌋ ,代入上面的式子:
ans=i=1nj=1m[gcd(i,j)=1] a n s = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = 1 ]

又因为有:
gcd(i,j)=1k|gcd(i,j)μ(k) g c d ( i , j ) = 1 ⇔ ∑ k | g c d ( i , j ) μ ( k )

略微解释一下,因为莫比乌斯函数 μ(x) μ ( x ) 有如下性质:
d|xμ(d)={10x=1x>1 ∑ d | x μ ( d ) = { 1 x = 1 0 x > 1

所以上面的式子只有当 gcd(i,j)=1 g c d ( i , j ) = 1 时取值不为零,所以 gcd(i,j)=1 g c d ( i , j ) = 1 等价于 k|gcd(i,j)μ(k) ∑ k | g c d ( i , j ) μ ( k )

再次代入:

ans=i=1nj=1mk|gcd(i,j)μ(k) a n s = ∑ i = 1 n ∑ j = 1 m ∑ k | g c d ( i , j ) μ ( k )

此时,我们不再先枚举 i,j i , j ,我们先枚举 gcd g c d k k 的情况:
ans=k=1min(n,m)μ(k)i=1nj=1m[k|gcd(i,j)]

这时,后面两个 Σ Σ 的答案就是 i,j i , j 同时整除 k k 的情况,显然的,1n中,整除 k k 的数有nk个,同理, 1m 1 ∼ m 中有 mk ⌊ m k ⌋ 个整除 j j 的数。所以,后面两个Σ的答案为 nkmk ⌊ n k ⌋ ⌊ m k ⌋ ,故式子最后化简为:
ans=k=1min(n,m)μ(k)nkmk a n s = ∑ k = 1 m i n ( n , m ) μ ( k ) ⌊ n k ⌋ ⌊ m k ⌋

n,m n , m 回代,得到最终结果:
ans=k=1min(n,m)μ(k)adkbdkans=k=1min(n,m)μ(k)adkbdk a n s = ∑ k = 1 m i n ( n , m ) μ ( k ) ⌊ ⌊ a d ⌋ k ⌋ ⌊ ⌊ b d ⌋ k ⌋ a n s = ∑ k = 1 m i n ( n , m ) μ ( k ) ⌊ a d k ⌋ ⌊ b d k ⌋

此时,我们已经可以在 O(n) O ( n ) 的复杂度内计算出答案了,但是存在多组询问,所以我们需要进一步降低单词询问复杂度。

我们发现,在一段区间 k[l,r] k ∈ [ l , r ] 中, adk,bdk ⌊ a d k ⌋ , ⌊ b d k ⌋ 的值是一样的,而这样的区间有大约 n n 个,这段 [l,r] [ l , r ] 中的 Σ Σ 就可以直接计算,所以我们只需要 n n 的复杂度就能算出一次询问,总复杂度为 O(qn) O ( q n )

代码
#include<bits/stdc++.h>
#define ll long long
#define R register int
using namespace std;
const int M=5e4+5,N=5e4;
int p[M],miu[M],n;
bool check[M];
void getmiu()
{
    miu[1]=check[1]=1;
    R i,j,t;
    for(i=2;i<=N;++i)
    {
        if(!check[i])miu[i]=-1,p[++p[0]]=i;
        for(j=1;j<=p[0];++j)
        {
            t=p[j]*i;
            if(t>N)break;
            check[t]=1;
            if(i%p[j]==0){miu[t]=0;break;}
            miu[t]=-miu[i];
        }
        miu[i]+=miu[i-1];
    }
}
ll f(int a,int b,int d)
{
    a/=d,b/=d;
    ll ans=0;
    if(a>b)swap(a,b);
    for(int l=1,r;l<=a;l=r+1)
    r=min(a/(a/l),b/(b/l)),ans+=1ll*(miu[r]-miu[l-1])*(a/l)*(b/l);
    return ans;
}
void in(){getmiu();scanf("%d",&n);}
void ac(){R i,a,b,c;for(i=1;i<=n;++i)scanf("%d%d%d",&a,&b,&c),printf("%lld\n",f(a,b,c));}
int main()
{
    in();ac();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值