[BZOJ 2820] YY的gcd(莫比乌斯反演+数论分块)

题面

给定N, M,求\(1\leq x\leq N, 1\leq y\leq M\)且gcd(x, y)为质数的(x, y)有多少对。q组询问

分析

我们要求的是

\[\sum_{p \in P} \sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=p]$$(大写P表示质数集合)

根据$kgcd(i,j)=gcd(ki,kj)$,

$$原式=\sum_{p \in P} \sum_{i=1}^{\lfloor n/p \rfloor} \sum_{j=1}^{\lfloor m/p \rfloor} [gcd(i,j)=1]\]

又根据莫比乌斯反演里的一个常用结论(证明见BZOJ 2301)$$\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=1]= \sum_{d=1}^{min(n,m)} \mu(d ) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor$$

\[原式=\sum_{p \in P} \sum_{d=1}^{min( \lfloor n/p \rfloor, \lfloor m/p \rfloor)} \mu(d) \lfloor \frac{n}{pd} \rfloor \lfloor \frac{m}{pd} \rfloor
\]

令\(T=pd\),则\(d=\frac{T}{p}\)

改变求和顺序,$$原式=\sum_{T=1}^{min(n,m)} \sum_{p|t \ \cap \ p \in P} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \mu(\frac{T}{p})$$

\[=\sum_{T=1}^{min(n,m)}) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \sum_{p|t \ \cap \ p \in P} \mu(\frac{T}{p})
\]

令\(g(n)=\sum_{p|n \ \cap \ p \in P } \mu(\frac{n}{p})\)

\[原式=\sum_{T=1}^{min(n,m)}) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor g(T)
\]

前面的部分可以数论分块求解,考虑如何快速求出\(g(T)\)

对于每个质数\(p\),我们从1开始枚举\(j\),并保证\(jp \leq n\),然后用\(\mu(j)\)更新\(g(jp)\)的值。

由于\(1/1+1/2+1/3+...+1/n=O(logn)\),每次更新的复杂度是均摊\(O(\log n)\)的,而1~n的质数约概有\(\frac{n}{\ln n}\)个,所以预处理g函数的总时间复杂度为\(O(n)\)

总时间复杂度\(O(n+q\sqrt n)\)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 10000000
using namespace std;
typedef long long ll;
int t,n,m;
int cnt;
int prime[maxn+5];
bool vis[maxn+5];
int mu[maxn+5];
ll g[maxn+5];
ll sumg[maxn+5];
void sieve(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
mu[i]=-1;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=cnt;i++){
for(int j=1;j*prime[i]<=n;j++){
g[prime[i]*j]+=mu[j];
}
}
for(int i=1;i<=n;i++){
sumg[i]=sumg[i-1]+g[i];
}
} int cas;
ll calc(int n,int m){
int nn=min(n,m);
ll ans=0;
for(int l=1,r;l<=nn;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=(sumg[r]-sumg[l-1])*(n/l)*(m/l);
}
return ans;
}
int main(){
sieve(maxn);
scanf("%d",&cas);
while(cas--){
scanf("%d %d",&n,&m);
printf("%lld\n",calc(n,m));
}
}

最新文章

  1. Unity3D研究院之动态修改烘培贴图的大小&amp;脚本烘培场景
  2. diy-pagination-javascript 分页
  3. redis使用日志(二) 数据存储到redis
  4. Bmob第三方登录详解
  5. mysql并发复制系列 一:binlog组提交
  6. mysql-5.7.10-winx64 安装时遇到的问题
  7. 【Xamarin挖墙脚系列:Android最重要的命令工具ADB】
  8. Oracle dblink的创建及使用
  9. java_设计模式_外观模式_Facade Pattern(2016-08-09)
  10. 64位sql server 如何使用链接服务器连接Access
  11. SSH小结
  12. mybatis sql循环的使用
  13. spring+mybatis+c3p0数据库连接池或druid连接池使用配置整理
  14. python使用正则解析网络地址的各个部分
  15. NeuChar 平台使用及开发教程(一):开始使用 NeuChar
  16. delphi 自动获取串口
  17. Scala环境搭建及Intellij IDEA安装
  18. Westore 1.0 正式发布 - 小程序框架一个就够
  19. Pycharm安装pygame游戏库遇到的问题
  20. APP安全在线检测

热门文章

  1. Proxy + Reflect 实现 响应的数据变化
  2. java编写算法题格式(链表和二叉树)
  3. Linux下升级安装Python-3.6.9版本
  4. 【leetcode】1095. Find in Mountain Array
  5. python-unittest生成报告的几种方式
  6. Java——IO
  7. DOS命令里面的EQ、NE、GT、LT、GE、LE分别是什么意思
  8. [bzoj]2705: [SDOI2012]Longge的问题[数论][数学][欧拉函数][gcd]
  9. 前端String类型转JSON类型
  10. 判断内网机器的真实外网IP或域名的方法总结