标签:class 提示 limit gcd bit scan clu line 质数
神犇YY虐完数论后给傻×kAc出了一题
给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对
kAc这种傻×必然不会了,于是向你来请教……
多组输入
第一行一个整数T 表述数据组数
接下来T行,每行两个正整数,表示N, M
T行,每行一个整数表示第i组数据的结果
T = 10000
N, M <= 10000000
2
10 10
100 100
30
2791
这道题类似P3455[POI2007]ZAP-Queries
本题其实是求解
\[\sum\limits_{p\in prime}\sum\limits_{x=1}^N\sum\limits_{y=1}^M[\gcd(x,y)==p]\]
大概就是看见\([\gcd(x,y)==p]\) 就想到\([\gcd(x,y)==1]\) 可以变成\(I=\mu *u\)
\[\sum\limits_{p\in prime}\sum\limits_{x=1}^{N/p}\sum\limits_{y=1}^{M/p}[\gcd(x,y)==1]\]
\[\sum\limits_{p\in prime}\sum\limits_{x=1}^{N/p}\sum\limits_{y=1}^{M/p}\sum\limits_{d|\gcd(x,y)}\mu(d)\]
然后整除变枚举
\[\sum\limits_{p\in prime}\sum\limits_{x=1}^{N/p}\sum\limits_{y=1}^{M/p}\sum\limits_{d=1}^{\min(N/p,M/p)}\mu(d)[d|\gcd(x,y)]\]
\[\sum\limits_{p\in prime}\sum\limits_{d=1}^{\min(N/p,M/p)}\mu(d)\sum\limits_{x=1}^{N/p}\sum\limits_{y=1}^{M/p}[d|\gcd(x,y)]\]
要想\(d|\gcd(x,y)\) ,当且仅当,\(d\) 同时是 \(x,y\) 的公因子
\(\lfloor\frac{N}{d}\rfloor\)里有\(\lfloor\frac{N}{dp}\rfloor\)个\(d\) 的倍数,同理,\(\lfloor\frac{M}{d}\rfloor\)里有\(\lfloor\frac{M}{dp}\rfloor\)个\(d\) 的倍数
乘法公式,总共就有\(\lfloor\frac{N}{dp}\rfloor\lfloor\frac{M}{dp}\rfloor\),就能化简成
\[\sum\limits_{p\in prime}\sum\limits_{d=1}^{\min(N/p,M/p)}\mu(d)\lfloor\frac{N}{dp}\rfloor\lfloor\frac{M}{dp}\rfloor\]
奇思妙想一下,更换一下枚举,\(T=dp\),就能敲了
\[\sum\limits_{T=1}^{\min(N,M)}\lfloor\frac{N}{T}\rfloor\lfloor\frac{M}{T}\rfloor\sum\limits_{p|T,p\in prime}\mu(\frac{T}{p})\]
前面的是分块,后面的是前缀和变形
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e7;
int prime[maxn+10];
int vis[maxn+10];
int mu[maxn+10];
int pmu[maxn+10];
ll sum[maxn+10];
void sieve(){
mu[1]=1;
int p=0;
for(int i=2;i<=maxn;i++){
if(!vis[i]){
vis[i]=1;
prime[++p]=i;
mu[i]=-1;
}
for(int j=1;j<=p&&i*prime[j]<=maxn;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=p;i++){
for(int j=1;j*prime[i]<=maxn;j++){
pmu[j*prime[i]]+=mu[j];
}
}
for(int i=1;i<=maxn;i++){
sum[i]=sum[i-1]+(ll)pmu[i];
}
}
int main(){
memset(vis,0,sizeof(vis));
sieve();
int t,n,m;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
ll ans=0;
int t=min(n,m);
int r;
for(int i=1;i<=t;){
r=min(n/(n/i),m/(m/i));
ans+=(ll)(n/i)*(m/i)*(sum[r]-sum[i-1]);
i=r+1;
}
cout<<ans<<endl;
}
}
标签:class 提示 limit gcd bit scan clu line 质数
原文地址:https://www.cnblogs.com/tongseli/p/11653238.html