洛谷 3455 (莫比乌斯反演优化)

 P3455 [POI2007]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, bband dd, find the number of integer pairs (x,y)(x,y) satisfying the following conditions:

1\le x\le a1≤x≤a,1\le y\le b1≤y≤b,gcd(x,y)=dgcd(x,y)=d, where gcd(x,y)gcd(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 nn (1\le n\le 50\ 0001≤n≤50 000),denoting the number of queries.

The following nn lines contain three integers each: aa, bb and dd(1\le d\le a,b\le 50\ 0001≤d≤a,b≤50 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

题解:运用莫比乌斯反演

后来发现直接加会TLE,必须优化,对于i~n/(n/i)范围内的值,n/i是相等的,利用该定理可减小时间复杂度

例:n=10

iji~jn/(i~j)
110/(10/1)=11~110
210/(10/2)=22~25
310/(10/3)=33~33
410/(10/4)=54~52
610/(10/6)=106~101
#include<iostream>
#include<string.h>
#define ll long long
using namespace std;
ll mu[50007],prime[50007],sum[50007];
bool mark[50007];
void getmu()
{
        mu[1]=1;
        ll cnt=0;
        for(ll i=2;i<50007;i++){
                if(!mark[i]){
                        prime[cnt++]=(ll)i;
                        mu[i]=-1;
                }
                for(ll j=0;j<cnt&&i*prime[j]<50007;j++){
                        mark[i*prime[j]]=1;
                        if(i%prime[j]){
                                mu[i*prime[j]]=-mu[i];
                        }else{
                                mu[i*prime[j]]=0;
                                break;
                        }
                }
        }
        for(int i=1;i<50007;i++)
                sum[i]=sum[i-1]+mu[i];
}
int main()
{
        int T;
        ll b,d,k,ans;
        getmu();
        scanf("%d",&T);
        for(int ca=1;ca<=T;ca++){
                scanf("%lld%lld%lld",&b,&d,&k);
                if(!k){printf("0\n");continue;}
                b/=k,d/=k;
                if(b>d) swap(b,d);
                ans=0;
                for(int x=1,y;x<=b;x=y+1){
                        y=min(b/(b/x),d/(d/x));
                        ans+=(sum[y]-sum[x-1])*(b/x)*(d/x);
                }
                printf("%lld\n",ans);
        }
        return 0;
}

 

转载于:https://www.cnblogs.com/aeipyuan/p/10262676.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值