P2257 YY的GCD(莫比乌斯反演)

P2257 YY的GCD

luogu题解第一篇非常棒,当然你也可以point here(转)

正题因为题解写的太优秀所以没得补充

这里用了一个卡常技巧:循环展开

就是以代码长度为代价减少循环次数

实测快了15ms(一个点1.5ms....)

(用了快读更慢???(大雾)但是register真有用)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#define re register
using namespace std;
template<typename T>T min(T &a,T &b){return a<b?a:b;}
#define N 10000001
typedef long long ll;
int t,n,m,pct,pri[N],mu[N],g[N];
ll sum[N],ans; bool v[N];
int main(){
    mu[1]=1;
    for(re int i=2;i<N;++i){
        if(!v[i]) pri[++pct]=i,mu[i]=-1;
        for(re int j=1;j<=pct;++j){
            int tmp=i*pri[j];
            if(tmp>=N) break;
            v[tmp]=1;
            if(i%pri[j]) mu[tmp]=-mu[i];
            else break;
        }
    }//线性筛莫比乌斯函数
    for(re int i=1;i<=pct;++i)
        for(re int j=1;1ll*j*pri[i]<N;++j)
            g[j*pri[i]]+=mu[j]; //这个数的莫比乌斯函数之和
    re int u; //循环展开:累计前缀和
    for(u=1;u+4<N;u+=4){
        sum[u]=sum[u-1]+g[u];
        sum[u+1]=sum[u]+g[u+1];
        sum[u+2]=sum[u+1]+g[u+2];
        sum[u+3]=sum[u+2]+g[u+3];
    }
    for(;u<N;++u) sum[u]=sum[u-1]+g[u];
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        if(n>m) swap(n,m);
        ans=0;
        for(re int l=1,r;l<=n;l=r+1){//整除分块
            r=min(n/(n/l),m/(m/l));
            ans+=1ll*(n/l)*(m/l)*(sum[r]-sum[l-1]);
        }printf("%lld
",ans);
    }return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/9790153.html