zoukankan      html  css  js  c++  java
  • bzoj千题计划203:bzoj3994: [SDOI2015]约数个数和

    http://www.lydsy.com/JudgeOnline/problem.php?id=3994

     设d(x)为x的约数个数,给定N、M,求

     

     

    用到的一个结论:

    证明:

    枚举n的约数i,枚举m的约数j

    那么i*j一定是n*m的约数

    d(nm)相当于不同的i*j 的个数

    若i, j 不互质

    设gcd(i,j)= g , 则 i= p*g ,j=q*g

    那么i*j 可以 组成两个互质数p*g*g 和 q 的乘积

    p*g*g 和 q 也都输n和m的约数

    即p*g*g 和 q 也都是合法的i,j

    所以 互质数i和j的乘积组成了n*m的所有的约数

    上式得证

     

    回到这个题

    令N<=M

       

    化为

    改变枚举顺序,先枚举i,j

    当n=[1,N]中所有i的倍数时,i会取n/i次

    即i会取 次    ( 里面有除号的[]表示下取整,下面一样)

     

    j 同理

    所以 上式化为

     

     

    =  

    =

    改变枚举顺序,先枚举d

    令 

    ans=

    f(x) 可以 用除法分块 提前 N*sqrt(N)处理好

    预处理 μ 的前缀和

    最后的式子 也可以用除法分块 在sqrt(N)时间内计算出

      

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 50001
    
    typedef long long LL;
    
    int prime[N];
    bool vis[N];
    int miu[N],sum[N];
    
    LL f[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void premiu()
    {
        int cnt=0;
        miu[1]=1;
        for(int i=2;i<N;++i)
        {
            if(!vis[i])
            {
                prime[++cnt]=i;
                miu[i]=-1;
            }
            for(int j=1;j<=cnt;++j)
            {
                if(prime[j]*i>=N) break;
                vis[i*prime[j]]=true;
                if(i%prime[j]==0) break;
                miu[i*prime[j]]=-miu[i];
            }
        }
        for(int i=1;i<N;++i) sum[i]+=sum[i-1]+miu[i];
    }
    
    LL pref(int x)
    {
        LL tot=0;
        int j;
        for(int i=1;i<=x;i=j+1)
        {
            j=x/(x/i);
            tot+=(LL)(j-i+1)*(x/i);
        }
        return tot;
    }
    
    int main()
    {
        premiu();
        for(int i=1;i<N;++i) f[i]=pref(i);
        int T,n,m,j;
        LL ans; 
        read(T);
        while(T--)
        {
            ans=0;
            read(n); read(m);
            if(n>m) swap(n,m);
            for(int i=1;i<=n;i=j+1)
            {
                j=min(n/(n/i),m/(m/i));
                ans+=f[n/i]*f[m/i]*(sum[j]-sum[i-1]);
            } 
            cout<<ans<<'
    ';
        }
    }
  • 相关阅读:
    php使用redis的有序集合zset实现延迟队列
    php使用redis的几种常见方式和用法
    redis缓存雪崩,缓存穿透,缓存击穿的解决方法
    php操作redis数据库方法总结
    mysql 悲观锁与乐观锁的理解
    OAuth2.0 协议的理解
    windows下的mongodb安装与配置
    node.js中对 redis 的安装和基本操作
    node.js中对 mysql 进行增删改查等操作和async,await处理
    node.js中 koa 框架的基本使用方法
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8228465.html
Copyright © 2011-2022 走看看