zoukankan      html  css  js  c++  java
  • [SPOJ] DIVCNT2

    # 题目
    vjudge URL:[Counting Divisors (square) ](https://vjudge.net/problem/SPOJ-DIVCNT2)
    Let $sigma_0(n)$ be the number of positive divisors of $n$.

    For example, $sigma_0(1) = 1$, $sigma_0(2) = 2$ and $sigma_0(6) = 4$.

    Let $$S_2(n) = sum _{i=1}^n sigma_0(i^2).$$

    Given $N$, find $S_2(N)$.

    ###### Input
    First line contains $T$ ($1 le T le 10000$), the number of test cases.

    Each of the next $T$ lines contains a single integer $N$. ($1 le N le 10^{12}$)

    ###### Output
    For each number $N$, output a single line containing $S_2(N)$.

    ###### Example
    - Input
    >5
    1
    2
    3
    10
    100
    - Output
    > 1
    4
    7
    48
    1194
    - Explanation for Input
    $~~~~S_2(3) = sigma_0(1^2) + sigma_0(2^2) + sigma_0(3^2) = 1 + 3 + 3 = 7$

    ###### Information
    >There are 6 Input files.
    >- Input #1: $1 le N le 10000$, TL = 1s.
    >- Input #2: $1 le T le 800, 1 le N le 10^{8}$, TL = 20s.
    >- Input #3: $1 le T le 200, 1 le N le 10^{9}$, TL = 20s.
    >- Input #4: $1 le T le 40, 1 le N le 10^{10}$, TL = 20s.
    >- Input #5: $1 le T le 10, 1 le N le 10^{11}$, TL = 20s.
    >- Input #6: $T = 1, 1 le N le 10^{12}$, TL = 20s.

    ***My C++ solution runs in 5.3 sec. (total time)*** //呵呵

    ***Source Limit is 6 KB.****

    #### 题目分析
    $sigma_0(n)$表示$n$的约数个数,即求
    $$large sum_{i=1}^n
    sigma_0(i^2)$$
    $1<=n<=10^{12}$

    ##### 前言
    - mdzz,写了1h的Latex公式没保存。。因为机房电脑的烂CPU,本地测这道题的极限数据时崩溃了c
    - 这道题是真的卡常,最后点线性筛必须筛到很大,5e7能过,1e7、2e7都不行(可能因为我是大常数选手吧,悄悄打上卡常的FLAG)
    - 本地评测炸电脑,真的无语。。
    - 刚开始模了1e9+7 WA了好久。。。
    - 由于是求函数前缀和,又和杜教筛的题一起做的,就放在杜教筛/莫比乌斯反演里吧
    ##### 正文
    设$large n=prod_{i=1}^kp_i^{a_i}$
    则$$large sigma_0(n^2)=prod_{i=1}^k(2a_i+1)\=sum_{Sin{1,2,...,k}}2^{|S|}cdotprod_{iin S}a_i$$
    将$prod_{iin S}a_i$看作是 只由$color{blue}S内的下标所对应的素数$(乘起来)构成的$n$的约数的**个数**,因为每一个约数都对答案造成了对应的$2^{|S|}$的贡献,那么
    $$large sigma_0(n^2)=sum_{d|n}2^{omega(d)}$$其中$omega(d)$表示d的质因子的个数(想想)
    与此同时,我们又发现$2^{omega(d)}$实质上是$d$的质因子选或不选的方案数,也就是$n$的**无平方因子的约数**的个数,则
    $$large sigma_0(n^2)=sum_{d|n}sum_{k|d}|mu(k)|$$因为根据$mu$函数的定义,只有无平方因子数的函数值才为$1$或$-1$,加上绝对值就相当于统计了个数(有的题解也写的是$mu(k)^2$,个人认为第一眼看到这个平方会懵一会)
    $$large sum_{i=1}^nsigma_0(i^2)=sum_{i=1}^nsum_{d|i}sum_{k|d}|mu(k)|\=sum_{k=1}^n|mu(k)|sum_{k|d}sum_{d|i}1\=sum_{k=1}^n|mu(k)|sum_{k|d}lfloorfrac nd floor\=sum_{k=1}^n|mu(k)|sum_{d=1}^{lfloorfrac nk floor}lfloorfrac n{dk} floor\=sum_{k=1}^n|mu(k)|sum_{d=1}^{lfloorfrac nk floor}lfloorfrac {lfloorfrac nk floor}d floor\=sum_{k=1}^n|mu(k)|sum_{d=1}^{lfloorfrac nk floor}lfloorfrac {lfloorfrac nk floor}d floor$$
    - 先看第二个$largesum$,对于某一个$large{lfloorfrac nk floor}$的取值,把它记作$N$,就以$N$的范围做整除分块优化,$largeTheta(sqrt N)$的时间复杂度,那么外层还有一个求和,于是在外面也套一层整除分块优化,预处理出前$large n^{frac 23}$后时间复杂度为$largeTheta(n^{frac23})$
    - 此处预处理为线性筛,考虑变换,$largesum_{i=1}^nlarge{lfloorfrac ni floor}$实际可看作枚举$i$后看$n$以内有多少个数能被$i$整除,这不就是$largesum_{i=1}^nsigma_0(i)$吗?
    于是我们只需要筛出约数个数在累加就行了,线性筛时存一下当前数的最小质因子的次数就可以愉快的线性筛了
    - 由于在外面一层套上了整除分块优化,则需要求出$large |mu(k)|$的前缀和,也就是$n$以内的无平方因子数
    - 这里处理无平方因子数时用容斥原理,有
    $$largesum_{i=1}^n|mu(i)|=sum_{i=1}^{sqrt n}mu(i)cdotlfloorfrac n{i^2} floor$$想想$mu$函数的定义,这个容斥还是比较好理解的
    $large Theta(sqrt n)$可处理出来

    综上,各种操作之后把时间复杂度降到了$largeTheta(n^{frac 23})$

    等等,真的降到了吗??!看看降到$largeTheta(n^{frac 23})$的条件?
    - 预处理出前$large n^{frac 23}$

    然而$1<=n<=10^{12}$(掀桌)
    所以只能尽可能的接近,实测$5e7$能过,$2e7$都会TLE

    ##### AC code

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN = 5e7 + 1;//!!!
    int Prime[MAXN], mu[MAXN], d[MAXN], Min_a[MAXN], Cnt;
    bool IsnotPrime[MAXN];
    LL sum_d[MAXN], sum_mu[MAXN];
    void init(int n)//线性筛,Min_a[i]存的是i最小质因子的次数
    {
        d[1] = mu[1] = 1;
        for(int i = 2; i <= n; ++i)
        {
        
            if(!IsnotPrime[i])
                Prime[++Cnt] = i, mu[i] = -1, d[i] = 2, Min_a[i] = 1;
            for(int j = 1, v; j <= Cnt && i * Prime[j] <= n; ++j)
            {
                v = i * Prime[j];
                IsnotPrime[v] = 1; Min_a[v] = 1;
                if(i % Prime[j] == 0)
                {
                    Min_a[v] += Min_a[i];
                    mu[v] = 0;
                    d[v] = d[i] / Min_a[v] * (Min_a[v] + 1);
                    break;
                }
                mu[v] = -mu[i];
                d[v] = d[i]<<1;
            }
        }
        for(int i = 1; i <= n; ++i)
            sum_d[i] = sum_d[i-1] + d[i],
            sum_mu[i] = sum_mu[i-1] + mu[i]*mu[i];
    }
    inline LL Sum_mu(LL n)//莫比乌斯函数的绝对值的前缀和/[1,n]无平方因子数个数
    {
        if(n < MAXN) return sum_mu[n];
        LL ret = 0;
        for(LL i = 1; i*i <= n; ++i)
            ret += mu[i] * (n/(i*i));
        return ret;
    }
    inline LL Sum_d(LL n) //约数个数前缀和
    {
        if(n < MAXN) return sum_d[n];
        LL ret = 0;
        for(LL i = 1, j; i <= n; i=j+1)
        {
            j = n/(n/i);
            ret += (n/i) * (j-i+1);
        }
        return ret;
    }
    
    inline LL solve(LL n)
    {
        LL ret = 0;
        for(LL i = 1, j; i <= n; i=j+1)
        {
            j = n/(n/i);
            ret += (Sum_mu(j)-Sum_mu(i-1)) * Sum_d(n/i);
        }
        return ret;
    }
    
    int main ()
    {
        LL T, n;
        scanf("%lld", &T);
        init(T > 800 ? 10000 : MAXN-1); //优化
        while(T--)
        {
            scanf("%lld", &n);
            printf("%lld
    ", solve(n));
        }
    }
    View Code

    参见 [传送门:大佬博客](https://www.cnblogs.com/qzqzgfy/p/5600088.html)
    ...
    ...
    ...
    ...
    ...

    ***再次吐槽数学公式的难打***

  • 相关阅读:
    Go strings.Builder
    httprouter使用pprof
    大规模分布式系统的跟踪系统
    Yearning 介绍(SQL审核平台)
    Inception介绍(MySQL自动化运维工具)
    go 学习资源和GitHub库
    go 命令
    Redash 安装部署
    systemd 编写
    查看mysql 版本
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039457.html
Copyright © 2011-2022 走看看