P3327/bzoj3994 [SDOI2015]约数个数和(莫比乌斯反演)

P3327 [SDOI2015]约数个数和

神犇题解(转)

无话可补

#include<iostream>
#include<cstdio>
#include<cstring>
#define re register
using namespace std;
template<typename T>T max(T &a,T &b){return a>b?a:b;}
template<typename T>T min(T &a,T &b){return a<b?a:b;}
#define N 50001
int t,n,m,pct,pri[N],mu[N],sum[N];
long long g[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){
            re int tmp=i*pri[j];
            if(tmp>=N) break;
            v[tmp]=1;
            if(i%pri[j]) mu[tmp]=-mu[i];
            else break;
        }//线性筛
    }re int u;
    for(u=1;u+4<N;u+=4){
        sum[u]=sum[u-1]+mu[u];
        sum[u+1]=sum[u]+mu[u+1];
        sum[u+2]=sum[u+1]+mu[u+2];
        sum[u+3]=sum[u+2]+mu[u+3];
    }//循环展开:微小加速
    for(;u<N;++u) sum[u]=sum[u-1]+mu[u];
    for(re int i=1;i<N;++i){
        ans=0;
        for(re int l=1,r;l<=i;l=r+1){
            r=i/(i/l);
            ans+=1ll*(r-l+1)*(i/l);
        }g[i]=ans;
    }
    
    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*(sum[r]-sum[l-1])*g[n/l]*g[m/l];
        }printf("%lld\n",ans);
    }return 0;
}

 

posted @ 2018-10-15 16:41  kafuuchino  阅读(123)  评论(0编辑  收藏  举报