题目
2820:YY的GCD
TimeLimit:10Sec
MemoryLimit:512MB
Description
神犇YY虐完数论后给傻×kAc出了一题
给定
N,M
,求
1≤x≤N,1≤y≤M
且
gcd(x,y)
为质数的
(x,y)
有多少对
kAc这种傻×必然不会了,于是向你来请教……多组输入
Input
第一行一个整数
T
表述数据组数接下来
Output
T
行,每行一个整数表示第
SampleInput
2
10 10
100 100
SampleOutput
30
2791
HINT
T=10000
N,M≤10000000
题解
这题可以猜到这是莫反题
然而并不需要知道这是莫反题
我们假设
N≤M
肯定要枚举一个质数,然后莫反可得:
ans=∑isprime(p)∑i=1n∑j=1m[gcd(i,j)=p]
=∑isprime(p)∑i=1⌊np⌋∑j=1⌊mp⌋[gcd(i,j)=1]
=∑isprime(p)∑i=1⌊np⌋∑j=1⌊mp⌋∑d|i∧d|jμ(d)
=∑isprime(p)∑d=1⌊np⌋μ(d)⌊npd⌋⌊mpd⌋
对于 μ(d)⌊npd⌋⌊mpd⌋ 来说,可以分块求和,复杂度 O(nn−√) 。
继续优化,设 k=pd ,可得:
ans=∑k=1n⌊nk⌋⌊mk⌋∑isprime(p)∧p|kμ(kp)
可以分块求和加预处理。
由 O(∑ni=11i)=O(log(n)) 可得,处理一个质数平均复杂度为 O(log(n)) 。由 n 以内的质数有
总结
对于莫反的题,可以用分块优化,也可以用预处理优化,总之可以一直优化。
标程
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=10000005;
bool vis[N];
ll miu[N];
ll pri[N];
ll sum[N];
ll t, top;
ll a, b;
void init()
{
miu[1]=1;
for (ll i=2; i<=10000000; i++)
{
if (!vis[i])
{
pri[++top]=i;
miu[i]=-1;
}
for (ll j=1; j<=top; j++)
{
if (i*pri[j]>10000000) break;
vis[i*pri[j]]=1;
if (i%pri[j]==0) break;
miu[i*pri[j]]=-miu[i];
}
}
for (ll i=1; i<=top; i++)
for (ll j=1; j*pri[i]<=10000000; j++)
sum[j*pri[i]]+=miu[j];
for (ll i=1; i<=10000000; i++) sum[i]+=sum[i-1];
}
ll calc(ll x, ll y)
{
if (x>y) swap(x, y);
ll ans=0, pos=0;
for (ll i=1; i<=x; i=pos+1)
{
pos=min(x/(x/i), y/(y/i));
ans+=(sum[pos]-sum[i-1])*(x/i)*(y/i);
}
return ans;
}
void work()
{
scanf("%lld%lld", &a, &b);
ll ans=calc(a, b);
printf("%lld\n", ans);
}
int main()
{
init();
scanf("%lld", &t);
while (t--) work();
return 0;
}
友情链接
bzoj2820解题报告
(大神比较喜欢黑色)
xxcc
大神解题报告
(又是那个王小花)
(又是那个很巨的王小花)
(又是那个叫我姐姐的王小花)
若有疑惑,问大神去。