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

[SDOI2015]约数个数和

时间:2018-03-29 02:13:14      阅读:231      评论:0      收藏:0      [点我收藏+]

标签:lld   ++   else   splay   了解   tchar   org   turn   char   

原题链接

题目描述

  • \(d(x)\)\(x\)的约数个数,给定\(N、M\),求\(\sum_{i=1}^{N}\sum_{j=1}^{m}d(ij)\)

输入输出格式

  • 输入格式:
  • 输入文件包含多组测试数据。第一行,一个整数T,表示测试数据的组数。接下来的T行,每行两个整数N、M。
  • 输出格式:
  • T行,每行一个整数,表示你所求的答案。

解题思路

  • 这道题,如果是第一次做,或者是不了解\(d(x)\)这个约数个数函数的某些神奇性质,那么是很难往下面继续推的。(我也是最近才知道的2333)
  • 很显然,这题的难点就是这个约数个数函数。因此,我先给出这个函数的一个重要性质:
    \[d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\]
    至于具体的证明,还是感性的理解一下吧。
  • 知道这个式子后,我们就可以开始尝试去推了。
  • 我们极其套路的去设几个函数(PS:如果不知道为什么要这样设,可以去看一看YY的GCD):
    \[f(d)=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=d]\]
    \[F(n)=\sum_{n|d}f(d)\]
    由莫比乌斯反演可以得到:
    \[f(n)=\sum_{n|d}\mu(\lfloor\frac{d}{n}\rfloor)F(d)\]
    我们所求的为Ans:
    \[Ans=\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)\]
    \[Ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\]
    看到这个\([gcd(x,y)=1]\),我们就可以根据\(\mu\)的性质把它带进去,如果不知道的话可以去看看我写的莫比乌斯反演
    \[Ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}\sum_{d|gcd(x,y)}\mu(d)\]
    更换枚举项,由枚举\(gcd(x,y)\)的约数,改为直接枚举\(d\)
    \[Ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}\sum_{d=1}^{min(n,m)}\mu(d)*[d|gcd(x,y)]\]
    \(\mu(d)\)可以提出来,因为它与\(i,j\)无关
    \[Ans=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[d|gcd(x,y)]\]
    接着,由枚举\(i,j\)和它们的约数改变为,枚举它们的约数再直接乘上这些约数的倍数的个数。因为每一个约数都会对它的倍数产生贡献。
    \[Ans=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^{n}\sum_{y=1}^{m}[d|gcd(x,y)]\lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{y}\rfloor\]
    我们再一次更换枚举项,将枚举\(x,y\)换为枚举\(dx,dy\)。这样\([d|gcd(x,y)]\)这个条件就可以省去。
    \[Ans=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y=1}^{{\lfloor\frac{m}{d}\rfloor}}\lfloor\frac{n}{dx}\rfloor\lfloor\frac{m}{dy}\rfloor\]
    我们可以发现\(\lfloor\frac{n}{dx}\rfloor\)\(y\)无关,所以可以提前。
    \[Ans=\sum_{d=1}^{min(n,m)}\mu(d)(\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{n}{dx}\rfloor)(\sum_{y=1}^{{\lfloor\frac{m}{d}\rfloor}}\lfloor\frac{m}{dy}\rfloor)\]
    当我们将式子化简成这样的时候,我们已经可以看出,这个式子已经可以做到\(O(n)\)计算了。但是,由于存在多组数据,所以我们就可以运用整除分块,将这个式子优化成\(O(\sqrt{n})\)的时间复杂度。(如果不知道整除分块,可以去看看我写的整除分块)
  • 这样,这道题就可以A了。

还是贴一下我的代码吧

#include<bits/stdc++.h>
#define N 50100
using namespace std;
inline void read(int &x)
{
    x=0;
    static int p;p=1;
    static char c;c=getchar();
    while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
    x*=p;
}
bool vis[N];
int prim[N],cnt,mu[N],sum[N];
long long g[N];
void get_mu(int n)
{
    mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]){prim[++cnt]=i;mu[i]=-1;}
        for(int j=1;j<=cnt&&prim[j]*i<=n;j++)
        {
            vis[prim[j]*i]=1;
            if(i%prim[j]==0)break;
            else mu[i*prim[j]]=-mu[i];
        }
    }
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+mu[i];
    for(int i=1;i<=n;i++)
    {
        long long ans=0;
        for(int l=1,r;l<=i;l=r+1)
        {
            r=(i/(i/l));
            ans+=1ll*(r-l+1)*1ll*(i/l);
        }
        g[i]=ans;
    }
}
int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    int t;
    read(t);
    get_mu(50000);
    while(t--)
    {
        static int n,m;
        read(n);read(m);
        static int max_rep;max_rep=min(n,m);
        static long long ans;ans=0;
        for(int l=1,r;l<=max_rep;l=r+1)
        {
            r=min(n/(n/l),m/(m/l));
            ans+=(sum[r]-sum[l-1])*1ll*g[n/l]*1ll*g[m/l];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

[SDOI2015]约数个数和

标签:lld   ++   else   splay   了解   tchar   org   turn   char   

原文地址:https://www.cnblogs.com/peng-ym/p/8667321.html

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