• BZOJ3994: [SDOI2015]约数个数和


    【传送门:BZOJ3994


    简要题意:

      给出n,m,设d(x)为x的约数个数,求$sum_{i=1}^{n}sum_{j=1}^{m}d(i*j)$


    题解:

      莫比乌斯反演,设n<m

      yy一下可以发现$d(i*j)=sum_{x|i}sum_{y|j}1[gcd(x,y)==1]$

      然后原式就等于$$sum_{i=1}^{n}sum_{j=1}^{m}sum_{x|i}sum_{y|j}1[gcd(x,y)==1]$$

      先枚举x,y,得到$$sum_{x=1}^{n}sum_{y=1}^{m}frac{n}{x}*frac{m}{y}[gcd(x,y)==1]$$

      因为$sum_{d|x}mu(d)=[x==1]$

      所以得到$$sum_{x=1}^{n}sum_{y=1}^{m}frac{n}{x}*frac{m}{y}sum_{d|gcd(x,y)}mu(d)$$

      先枚举d,得到$$sum_{d=1}^{n}mu(d)sum_{x=1}^{n}[d|x]sum_{y=1}^{m}[d|y]frac{n}{x}*frac{m}{y}$$

      $$sum_{d=1}^{n}mu(d)sum_{x=1}^{frac{n}{d}}frac{n}{d*x}*sum_{y=1}^{frac{m}{d}}frac{m}{d*y}$$

      显然最前面一项可以预处理,设$S(x)=sum_{i=1}^{x}frac{x}{i}$,得到$$sum_{d=1}^{n}mu(d)*S(frac{n}{d})*S(frac{m}{d})$$

      S(x)可以$O(n*sqrt{n})$预处理,但是实际上可以在线性筛的时候顺便处理

      因为我们发现$S(x)=sum_{i=1}^{x}d(i)$,只要求出d(i)就可以了


    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    int miu[51000],prime[51000],v[51000];
    LL d[51000];int c[51000];
    void pre(int n)
    {
        int m=0;miu[1]=d[1]=c[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(v[i]==0)
            {
                v[i]=i;c[i]=1;d[i]=2;
                prime[++m]=i;
                miu[i]=-1;
            }
            for(int j=1;j<=m;j++)
            {
                if(prime[j]>v[i]||prime[j]>n/i) continue;
                v[i*prime[j]]=prime[j];
                if(i%prime[j]==0) miu[i*prime[j]]=0,d[i*prime[j]]=d[i]/(c[i]+1)*(c[i]+2),c[i*prime[j]]=c[i]+1;
                else miu[i*prime[j]]=-miu[i],d[i*prime[j]]=d[i]*d[prime[j]],c[i*prime[j]]=1;
            }
        }
        for(int i=1;i<=n;i++) miu[i]+=miu[i-1],d[i]+=d[i-1];
    }
    int main()
    {
        pre(50000);
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m;
            scanf("%d%d",&n,&m);if(n>m) swap(n,m);
            LL ans=0;
            for(int i=1,j;i<=n;i=j+1)
            {
                j=min(n/(n/i),m/(m/i));
                ans+=(LL)(miu[j]-miu[i-1])*d[n/i]*d[m/i];
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }

     

  • 相关阅读:
    在VS2010中如何添加MSCOMM控件,实现串口通讯
    GroupBox与Panel控件
    如何在VS2010中添加ActiveX控件及使用方法
    如何在vs2010中添加Picture控件
    四线开发经验谈
    socket 、 udp 和 tcp
    文本文件与二进制文件
    文件读写 (流)
    [CTSC1999]家园
    洛谷 P1251 餐巾计划问题
  • 原文地址:https://www.cnblogs.com/Never-mind/p/9847862.html
Copyright © 2020-2023  润新知