ZAP-Queries(洛谷-P3455)

题目描述

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

输入输出格式

输入格式:

第一行一个整数T 表述数据组数

接下来T行,每行三个正整数,表示 a,b.d

输出格式:

T行,每行一个整数表示第 i 组数据的结果

输入输出样例

输入样例#1:

2
4 5 2
6 4 3

输出样例#1:

3
2

思路:本题思路与  YY的GCD(洛谷-P2257) 极其相似

实质是要求 res=\sum_{i=1}^a\sum_{j=1}^b[gcd(i,j)=d]

设 f(d) 为满足 GCD(i,j)=d 的个数,即:f(k)=\sum_{i=1}^a\sum_{j=1}^b[gcd(i,j)=d]

g(n) 为满足 GCD(i,j)=d 的倍数的个数,即:g(n)=\sum_{n|k}f(k)=\left \lfloor \frac{a}{n} \right \rfloor \left \lfloor \frac{b}{n} \right \rfloor

可以看出,g(n) 与 f(d) 间满足莫比乌斯反演的形式:g(n)=\sum_{n|d}f(d) \Leftrightarrow f(n)=\sum_{n|d} u(\frac{d}{n})g(d)

那么,对 res=\sum_{i=1}^n\sum_{j=1}^m[gcd(x,y)=d] 进行化简

将 f(k) 代入,有:res=f(d)

根据莫比乌斯反演:res=\sum_{d|k} u(\frac{k}{d})g(k)=\sum_{d|k} u(\frac{k}{d})\left \lfloor \frac{a}{k} \right \rfloor\left \lfloor \frac{b}{k} \right \rfloor

设枚举项 \left \lfloor \frac{k}{d} \right \rfloor 为 t

那么有:res=\sum_{t=1}^{min(a,b)}u(t)g(dt)=\sum_{t=1}^{min(a,b)}u(t)\left \lfloor \frac{n}{d*t} \right \rfloor\left \lfloor \frac{m}{d*t} \right \rfloor

进一步优化时间复杂,将 \left \lfloor \frac{n}{d*t} \right \rfloor\left \lfloor \frac{m}{d*t} \right \rfloor 中的 d 提出来,有:res=\sum_{t=1}^{min(\frac{a}{d},\frac{b}{d})}u(t)\left \lfloor \frac{n}{t} \right \rfloor\left \lfloor \frac{m}{t} \right \rfloor

此时的复杂度为 O(n),由于是多组查询,发现式子中有整除,利用整除分块求 mu 的前缀和,进行优化即可

源代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
const int MOD = 1E9+7;
const int N = 100000+5;
const int dx[] = {1,-1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
int mu[N];
int prime[N];
bool bprime[N];
int cnt;
LL sum[N];
void getMu(int n){//线性筛求莫比乌斯函数
    cnt=0;
    mu[1]=1;//根据定义,μ(1)=1
    memset(bprime,false,sizeof(bprime));

    for(int i=2;i<=n;i++){//求2~n的莫比乌斯函数
        if(!bprime[i]){
            prime[++cnt]=i;//存储质数
            mu[i]=-1;//i为质数时,μ(1)=-1
        }
        for(int j=1;j<=cnt&&i*prime[j]<=n;j++){//枚举i之前的素数个数
            bprime[i*prime[j]]=true;//不是质数
            if(i%prime[j])//i不是prime[j]的整数倍时,i*prime[j]就不会包含相同质因子
                mu[i*prime[j]]=-mu[i];//mu[k]=mu[i]*mu[prime[j]],因为prime[j]是质数,mu值为-1
            else{
                mu[i*prime[j]]=0;
                break;//留到后面再筛
            }
        }
    }

    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+mu[i];
}
LL g[N];
int main(){
    getMu(100000);

    int t;
    scanf("%d",&t);
    int Case=1;
    while(t--){
        int a,b,d;
        scanf("%d%d%d",&a,&b,&d);

        a/=d;
        b/=d;
        int minn=min(a,b);
        LL res=0;
        for(int left=1,right;left<=minn;left=right+1){
            right=min(a/(a/left),b/(b/left));
            res+=(1LL)*(a/left)*(b/left)*(sum[right]-sum[left-1]);
        }
        printf("%lld\n",res);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值