1. 程式人生 > >bzoj3994: [SDOI2015]約數個數和(莫比烏斯反演+分塊)

bzoj3994: [SDOI2015]約數個數和(莫比烏斯反演+分塊)

put name 一行 AI algorithm scan space 代碼 print

www.cnblogs.com/shaokele/


bzoj3994: [SDOI2015]約數個數和

  Time Limit: 20 Sec
  Memory Limit: 128 MB

Description

  設d(x)為x的約數個數,給定N、M,求 \(\sum_{i=1}^{n}\sum_{j=1}^{m}d(i j)\)
 

Input

  輸入文件包含多組測試數據。
  
  第一行,一個整數T,表示測試數據的組數。
  
  接下來的T行,每行兩個整數N、M。
 

Output

  T行,每行一個整數,表示你所求的答案。
 

Sample Input

  2
  
  7 4
  

  5 6
 

Sample Output

  110
  
  121
  

題目地址:  bzoj3994: [SDOI2015]約數個數和

題目大意:   題目很簡潔了:)

  

題解:

  莫比烏斯反演
  
  初寫莫隊可以先切這題
  


AC代碼

#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int N=500005;
int n,m,Q,tot;
int u[N],f[N],g[N],pri[N];
bool fl[N];
inline void init(){
    u[1]=f[1]=1;
    for(int i=2;i<=N;i++){
        if(!fl[i]){
            pri[++tot]=i;
            g[i]=1;
            u[i]=-1;
            f[i]=2;
        }
        for(int j=1;j<=tot && i*pri[j]<=N;j++){
            fl[i*pri[j]]=1;
            if(i%pri[j]==0){
                g[i*pri[j]]=g[i]+1;
                u[i*pri[j]]=0;
                f[i*pri[j]]=f[i]/(g[i]+1)*(g[i]+2);
            }else{
                g[i*pri[j]]=1;
                u[i*pri[j]]=-u[i];
                f[i*pri[j]]=f[i]*2;
            }
        }
    }
    for(int i=2;i<=N;i++)
        f[i]+=f[i-1],u[i]+=u[i-1];
}
int main(){
    init();
    scanf("%d",&Q);
    while(Q--){
        scanf("%d%d",&n,&m);
        if(n>m)swap(n,m);
        ll ans=0;
        int l,r;
        for(l=1;l<=n;l=r+1){
            r=min(n/(n/l),m/(m/l));
            ans+=(ll)(u[r]-u[l-1])*f[n/l]*f[m/l];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

bzoj3994: [SDOI2015]約數個數和(莫比烏斯反演+分塊)