BZOJ2820 YGY的GCD
•题目大意:求有多少数对(x,y)(1<=x<=n,1<=y<=m)满足gcd(x,y)为质数
T = 10000
N, M <= 10000000
参考popoqqqPPT:
首先,根据http://blog.youkuaiyun.com/viphong/article/details/52451540
f(x)表示gcd(i,j)==x的ij对数,F(x)是包含f(x)的
那么对于一个质数其贡献为 f(i)=
直接计算,光枚举p就已经TLE了。
我们可以发现,p是所有的质数,f(p)枚举了每个质数的所有倍数,这其实不就是把所有的数都枚举了吗:
也就是从
变成了
而我们发现,这个跟又跟那个分块优化的套路非常相似了
我们只需要预处理出 的前缀和,就可以sqrt(n)求到答案了
这个东西什么意思? 就是对于一个d, 把mu(d的约数)求和。
而这个我们可以预处理,枚举所有质数,去更新这个质数的所有倍数。
假设我们枚举的是n个数,那么每一个数的倍数有n/i个,一共枚举的数量就是=n*
,调和级数嘛,也就是n*lgn咯,似乎可以证明是均摊logn,而质数的个数约为O(n/lgn),因此
枚举所有质数的倍数的复杂度约为,o(logn*n/logn)=o(n)
再求个前缀和,所以预处理这一部分的复杂度是o(n)
综上复杂度为sqrt(n)
参考代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll p =1000000007;
const int N=10000000;
bool is_prime[N+500];
int prime[N+50];
int mu[N+50];
ll sum[N+50];
ll tot;
void Moblus()
{
tot = 0;
mu[1] = 1;
for(ll i = 2; i < N; i++)
{
if(!is_prime[i])
{
prime[tot++] = i;
mu[i] = -1;
}
for(ll j = 0; j < tot && i*prime[j] < N; j++)
{
is_prime[i*prime[j]] = 1;
if(i % prime[j])
{
mu[i*prime[j]] = -mu[i];
}
else
{
mu[i*prime[j]] = 0;
break;
}
}
}
}
ll ud[N+50];
void pre()
{
for (int i=0;i<tot;i++)
{
for (ll j=prime[i];j<=N;j+=prime[i])
{
ud[j]+=mu[j/prime[i]];
}
}
}
//找[1,n],[1,m]内gcd(i,j)为质数的对数
ll solve(ll n,ll m )
{
if (n>m)swap(n,m);
ll ret=0;
for (int i=1,last;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ret+=(sum[last]-sum[i-1])*(n/i)*(m/i);
}
return ret;
}
int main()
{
Moblus();
pre();
for (int i=1;i<N;i++)
sum[i]=sum[i-1]+ud[i];
freopen("YYnoGCD.in","r",stdin);
freopen("YYnoGCD.out","w",stdout);
int t;cin>>t;
while(t--)
{
ll n,m;
scanf("%lld%lld",&n,&m);
ll ans=solve(n,m);
printf("%lld\n",ans);
}
return 0;
}