码迷,mamicode.com
首页 > 其他好文 > 详细

P3327 [SDOI2015]约数个数和

时间:2019-10-12 01:27:24      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:play   约数个数   质因数分解   std   floor   sizeof   typedef   def   枚举   

题目描述

设d(x)为x的约数个数,给定N、M,求 \(\sum\limits^N_{i=1}\sum\limits^M_{j=1}d(ij)\)

输入格式

输入文件包含多组测试数据。第一行,一个整数T,表示测试数据的组数。接下来的T行,每行两个整数N、M。

输出格式

T行,每行一个整数,表示你所求的答案。

说明/提示

1<=N, M<=50000

1<=T<=50000

输入输出样例

输入

2
7 4
5 6

输出

110
121

首先,需要知道一个公式

\[d(ij)=\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)==1]\]

可以简单的意会一下这个公式。

我们对\(i\) 质因数分解,\(i=\prod\limits_{t}p_t^{\alpha_t}\) ,同样地,对\(j\) 质因数分解,\(j=\prod\limits_{t}p_t^{\beta_t}\)

我们显然知道,\(d(i)=\prod\limits_{t}(\alpha_t+1)\)\(d(i)=\prod\limits_{t}(\beta_t+1)\)

回到题目,\(ij=\prod\limits_{t}p_t^{\alpha_t+\beta_t}\) ,于是 \(d(ij)=\prod\limits_{t}(\alpha_t+\beta_t+1)\)

我们考虑直接这样枚举:\(\sum\limits_{x|i}\sum\limits_{y|j}1\)

这样就枚举多了...超过了\(\alpha_t+\beta_t+1\) ...

事实上,我们只需要考虑,\(ij\) 每个质因子\(p_t\) 被计算了几次(它应该被枚举\(\alpha_t+\beta_t\)次)。

不难发现,我们只要保证\(\gcd(x,y)==1\),就可以在\(p|a\) 的时候被枚举\(\alpha_t\) 次,在\(p|b\) 时枚举\(\beta_t\) 次。

所以就是

\[d(ij)=\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)==1]\]

本题是求

\[\sum\limits^N_{i=1}\sum\limits^M_{j=1}d(ij)\]

代入公式,可以有

\[\sum\limits^N_{i=1}\sum\limits^M_{j=1}\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)==1]\]

\[\sum\limits^N_{i=1}\sum\limits_{x|i}1\sum\limits^M_{j=1}\sum\limits_{y|j}1\cdot[\gcd(x,y)==1]\]

更换一下枚举顺序

\[\sum\limits^N_{x=1}\lfloor\frac{N}{x}\rfloor\sum\limits^M_{y=1}\lfloor\frac{M}{y}\rfloor\cdot[\gcd(x,y)==1]\]

\[\sum\limits^N_{x=1}\sum\limits^M_{y=1}[\gcd(x,y)==1]\cdot\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor\]

用一下\(I=\mu *u\)

\[\sum\limits^N_{x=1}\sum\limits^M_{y=1}\sum\limits_{d|gcd(x,y)}\mu(d)\cdot\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor\]

改成枚举

\[\sum\limits^N_{x=1}\sum\limits^M_{y=1}\sum\limits_{d=1}^{\min(N,M)}[d|gcd(x,y)]\mu(d)\cdot\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor\]

\[\sum\limits_{d=1}^{\min(N,M)}\mu(d)\sum\limits^N_{x=1}\sum\limits^M_{y=1}[d|gcd(x,y)]\cdot\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor\]

更换一下枚举到东西,本来是枚举\(x,y\),现在枚举\(dx,dy\) ,这样肯定会被整除!

\[\sum\limits_{d=1}^{\min(N,M)}\mu(d)\sum\limits^{N/d}_{x=1}\sum\limits^{M/d}_{y=1}\lfloor\frac{N}{dx}\rfloor\lfloor\frac{M}{dy}\rfloor\]

此时,我们就得到最终的式子

\[\sum\limits_{d=1}^{\min(N,M)}\mu(d)\sum\limits^{N/d}_{x=1}\lfloor\frac{N}{dx}\rfloor\sum\limits^{M/d}_{y=1}\lfloor\frac{M}{dy}\rfloor\]

打个分块,预处理一下后面两项,再前缀和一下\(\mu\) 就可。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 5e4;

int prime[maxn+10];
int vis[maxn+10];
int mu[maxn+10];
int sum[maxn+10];
ll f[maxn+10];


void sieve(){
    mu[1]=1;
    int p=0;
    for(int i=2;i<=maxn;i++){
        if(!vis[i]){
            vis[i]=1;
            prime[++p]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=p&&i*prime[j]<=maxn;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            else mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=maxn;i++){
        sum[i]=sum[i-1]+mu[i];
    }
}

void init(){
    for(int i=1;i<=maxn;i++){
        ll ans=0;
        for(int l=1;l<=i;){
            ll r=i/(i/l);
            ans+=(ll)(r-l+1)*(ll)(i/l);
            l=r+1;
        }
        f[i]=ans;
    }
}

int main(){
    memset(vis,0,sizeof(vis));
    sieve();
    init();
    int t,n,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        int tmp=min(n,m);
        ll res=0;
        for(int i=1;i<=tmp;){
            int r=min(n/(n/i),m/(m/i));
            res+=(ll)(sum[r]-sum[i-1])*(ll)f[n/i]*(ll)f[m/i];
            i=r+1;
        }
        cout<<res<<endl;
    }
}

P3327 [SDOI2015]约数个数和

标签:play   约数个数   质因数分解   std   floor   sizeof   typedef   def   枚举   

原文地址:https://www.cnblogs.com/tongseli/p/11657884.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!