【BZOJ】2820: YY的GCD

【题意】给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对。T<=10^4,N,M<=10^7。

【算法】数论(莫比乌斯反演)

【题解】公式推导见DQSSS

推到ans= Σp是素数 Σd≤mins μ(d) * (n/pd) * (m/pd),mins=min(n/p,m/p)。

使用枚举取值的方法再枚举素数单次询问复杂度√n*(n/ln n),显然不能满足要求。

问题在于枚举素数,令T=pd,则:

ans= ΣT≤mins (n/T) * (m/T)*Σp|T&&p是素数 μ(T/p),mins=min(n,m)。

后面部分可以枚举素数的倍数预处理出μ前缀和,复杂度O((n/ln n)*ln n)即O(n)。

每次询问再枚举取值O(√n)解决。

总复杂度O(T*√n+n)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=10000010,N=10000000;
int miu[maxn],mius[maxn],prime[maxn],tot;
ll s[maxn];
bool mark[maxn];
void pre(){
    miu[1]=1;
    for(int i=2;i<=N;i++){
        if(!mark[i])miu[prime[++tot]=i]=-1;
        for(int j=1;j<=tot&&i*prime[j]<=N;j++){
            mark[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            miu[i*prime[j]]=-miu[i];
        }
    }
    for(int i=1;i<=tot;i++){
        for(int j=prime[i];j<=N;j+=prime[i])mius[j]+=miu[j/prime[i]];
    }
    for(int i=1;i<=N;i++)s[i]=s[i-1]+mius[i];
}
int main(){
    pre();
    int T,n,m;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        int pos=0,mins=min(n,m);ll ans=0;
        for(int i=1;i<=mins;i=pos+1){
            pos=min(n/(n/i),m/(m/i));
            ans+=(s[pos]-s[i-1])*(n/i)*(m/i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

将枚举两个数改为枚举乘积和其中一个,即T和p|T,后面的p|T可以O(n log n)贡献处理前缀和。

posted @ 2018-01-12 09:50  ONION_CYC  阅读(284)  评论(0编辑  收藏  举报