[SPOJ] DIVCNT2

泪湿孤枕 提交于 2020-07-28 17:54:38

题目

vjudge URL:Counting Divisors (square)
Let σ 0 ( n ) \sigma_0(n) σ0(n) be the number of positive divisors of n n n.

For example, σ 0 ( 1 ) = 1 \sigma_0(1) = 1 σ0(1)=1, σ 0 ( 2 ) = 2 \sigma_0(2) = 2 σ0(2)=2 and σ 0 ( 6 ) = 4 \sigma_0(6) = 4 σ0(6)=4.

Let S 2 ( n ) = ∑ i = 1 n σ 0 ( i 2 ) . S_2(n) = \sum _{i=1}^n \sigma_0(i^2). S2(n)=i=1nσ0(i2).

Given N N N, find S 2 ( N ) S_2(N) S2(N).

Input

First line contains T T T ( 1 ≤ T ≤ 10000 1 \le T \le 10000 1T10000), the number of test cases.

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

Output

For each number N N N, output a single line containing S 2 ( N ) S_2(N) S2(N).

Example
  • Input

5
1
2
3
10
100




  • Output

1
4
7
48
1194



  • Explanation for Input
         S 2 ( 3 ) = σ 0 ( 1 2 ) + σ 0 ( 2 2 ) + σ 0 ( 3 2 ) = 1 + 3 + 3 = 7 ~~~~S_2(3) = \sigma_0(1^2) + \sigma_0(2^2) + \sigma_0(3^2) = 1 + 3 + 3 = 7     S2(3)=σ0(12)+σ0(22)+σ0(32)=1+3+3=7
Information

There are 6 Input files.

  • Input #1: 1 ≤ N ≤ 10000 1 \le N \le 10000 1N10000, TL = 1s.
  • Input #2: 1 ≤ T ≤ 800 ,   1 ≤ N ≤ 1 0 8 1 \le T \le 800,\ 1 \le N \le 10^{8} 1T800, 1N108, TL = 20s.
  • Input #3: 1 ≤ T ≤ 200 ,   1 ≤ N ≤ 1 0 9 1 \le T \le 200,\ 1 \le N \le 10^{9} 1T200, 1N109, TL = 20s.
  • Input #4: 1 ≤ T ≤ 40 ,   1 ≤ N ≤ 1 0 10 1 \le T \le 40,\ 1 \le N \le 10^{10} 1T40, 1N1010, TL = 20s.
  • Input #5: 1 ≤ T ≤ 10 ,   1 ≤ N ≤ 1 0 11 1 \le T \le 10,\ 1 \le N \le 10^{11} 1T10, 1N1011, TL = 20s.
  • Input #6: T = 1 ,   1 ≤ N ≤ 1 0 12 T = 1,\ 1 \le N \le 10^{12} T=1, 1N1012, TL = 20s.

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

Source Limit is 6 KB.*

题目分析

σ 0 ( n ) \sigma_0(n) σ0(n)表示 n n n的约数个数,即求
∑ i = 1 n σ 0 ( i 2 ) \large \sum_{i=1}^n \sigma_0(i^2) i=1nσ0(i2)
1 &lt; = n &lt; = 1 0 12 1&lt;=n&lt;=10^{12} 1<=n<=1012

前言
  • mdzz,写了1h的Latex公式没保存。。因为机房电脑的烂CPU,本地测这道题的极限数据时崩溃了c
  • 这道题是真的卡常,最后点线性筛必须筛到很大,5e7能过,1e7、2e7都不行(可能因为我是大常数选手吧,悄悄打上卡常的FLAG)
  • 本地评测炸电脑,真的无语。。
  • 刚开始模了1e9+7 WA了好久。。。
  • 由于是求函数前缀和,又和杜教筛的题一起做的,就放在杜教筛/莫比乌斯反演里吧
正文

n = ∏ i = 1 k p i a i \large n=\prod_{i=1}^kp_i^{a_i} n=i=1kpiai
σ 0 ( n 2 ) = ∏ i = 1 k ( 2 a i + 1 ) = ∑ S ∈ { 1 , 2 , . . . , k } 2 ∣ S ∣ ⋅ ∏ i ∈ S a i \large \sigma_0(n^2)=\prod_{i=1}^k(2a_i+1)\\=\sum_{S\in\{1,2,...,k\}}2^{|S|}\cdot\prod_{i\in S}a_i σ0(n2)=i=1k(2ai+1)=S{1,2,...,k}2SiSai
∏ i ∈ S a i \prod_{i\in S}a_i iSai看作是 只由 S 内 的 下 标 所 对 应 的 素 数 \color{blue}S内的下标所对应的素数 S(乘起来)构成的 n n n的约数的个数,因为每一个约数都对答案造成了对应的 2 ∣ S ∣ 2^{|S|} 2S的贡献,那么
σ 0 ( n 2 ) = ∑ d ∣ n 2 ω ( d ) \large \sigma_0(n^2)=\sum_{d|n}2^{\omega(d)} σ0(n2)=dn2ω(d)其中 ω ( d ) \omega(d) ω(d)表示d的质因子的个数(想想)
与此同时,我们又发现 2 ω ( d ) 2^{\omega(d)} 2ω(d)实质上是 d d d的质因子选或不选的方案数,也就是 n n n无平方因子的约数的个数,则
σ 0 ( n 2 ) = ∑ d ∣ n ∑ k ∣ d ∣ μ ( k ) ∣ \large \sigma_0(n^2)=\sum_{d|n}\sum_{k|d}|\mu(k)| σ0(n2)=dnkdμ(k)因为根据 μ \mu μ函数的定义,只有无平方因子数的函数值才为 1 1 1 − 1 -1 1,加上绝对值就相当于统计了个数(有的题解也写的是 μ ( k ) 2 \mu(k)^2 μ(k)2,个人认为第一眼看到这个平方会懵一会)
∑ i = 1 n σ 0 ( i 2 ) = ∑ i = 1 n ∑ d ∣ i ∑ k ∣ d ∣ μ ( k ) ∣ = ∑ k = 1 n ∣ μ ( k ) ∣ ∑ k ∣ d ∑ d ∣ i 1 = ∑ k = 1 n ∣ μ ( k ) ∣ ∑ k ∣ d ⌊ n d ⌋ = ∑ k = 1 n ∣ μ ( k ) ∣ ∑ d = 1 ⌊ n k ⌋ ⌊ n d k ⌋ = ∑ k = 1 n ∣ μ ( k ) ∣ ∑ d = 1 ⌊ n k ⌋ ⌊ ⌊ n k ⌋ d ⌋ = ∑ k = 1 n ∣ μ ( k ) ∣ ∑ d = 1 ⌊ n k ⌋ ⌊ ⌊ n k ⌋ d ⌋ \large \sum_{i=1}^n\sigma_0(i^2)=\sum_{i=1}^n\sum_{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}\lfloor\frac nd\rfloor\\=\sum_{k=1}^n|\mu(k)|\sum_{d=1}^{\lfloor\frac nk\rfloor}\lfloor\frac n{dk}\rfloor\\=\sum_{k=1}^n|\mu(k)|\sum_{d=1}^{\lfloor\frac nk\rfloor}\lfloor\frac {\lfloor\frac nk\rfloor}d\rfloor\\=\sum_{k=1}^n|\mu(k)|\sum_{d=1}^{\lfloor\frac nk\rfloor}\lfloor\frac {\lfloor\frac nk\rfloor}d\rfloor i=1nσ0(i2)=i=1ndikdμ(k)=k=1nμ(k)kddi1=k=1nμ(k)kddn=k=1nμ(k)d=1kndkn=k=1nμ(k)d=1kndkn=k=1nμ(k)d=1kndkn





  • 先看第二个 ∑ \large\sum ,对于某一个 ⌊ n k ⌋ \large{\lfloor\frac nk\rfloor} kn的取值,把它记作 N N N,就以 N N N的范围做整除分块优化, Θ ( N ) \large\Theta(\sqrt N) Θ(N )的时间复杂度,那么外层还有一个求和,于是在外面也套一层整除分块优化,预处理出前 n 2 3 \large n^{\frac 23} n32后时间复杂度为 Θ ( n 2 3 ) \large\Theta(n^{\frac23}) Θ(n32)
    • 此处预处理为线性筛,考虑变换, ∑ i = 1 n ⌊ n i ⌋ \large\sum_{i=1}^n\large{\lfloor\frac ni\rfloor} i=1nin实际可看作枚举 i i i后看 n n n以内有多少个数能被 i i i整除,这不就是 ∑ i = 1 n σ 0 ( i ) \large\sum_{i=1}^n\sigma_0(i) i=1nσ0(i)吗?
      于是我们只需要筛出约数个数在累加就行了,线性筛时存一下当前数的最小质因子的次数就可以愉快的线性筛了
  • 由于在外面一层套上了整除分块优化,则需要求出 ∣ μ ( k ) ∣ \large |\mu(k)| μ(k)的前缀和,也就是 n n n以内的无平方因子数
    • 这里处理无平方因子数时用容斥原理,有
      ∑ i = 1 n ∣ μ ( i ) ∣ = ∑ i = 1 n μ ( i ) ⋅ ⌊ n i 2 ⌋ \large\sum_{i=1}^n|\mu(i)|=\sum_{i=1}^{\sqrt n}\mu(i)\cdot\lfloor\frac n{i^2}\rfloor i=1nμ(i)=i=1n μ(i)i2n想想 μ \mu μ函数的定义,这个容斥还是比较好理解的
      Θ ( n ) \large \Theta(\sqrt n) Θ(n )可处理出来

综上,各种操作之后把时间复杂度降到了 Θ ( n 2 3 ) \large\Theta(n^{\frac 23}) Θ(n32)

等等,真的降到了吗??!看看降到 Θ ( n 2 3 ) \large\Theta(n^{\frac 23}) Θ(n32)的条件?

  • 预处理出前 n 2 3 \large n^{\frac 23} n32

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

CAUTION

首先做这道题,如果电脑是机房的老电脑/旧电脑/烂电脑,不要像作死去测什么 1 0 12 10^{12} 1012的极限数据,反正我是卡爆了(逃)
也不要像我一样边写代码边写博客,更不要卡住之后作死的乱点一气把浏览器搞炸了,我写了 1 h 1h 1h L a t e x Latex Latex啊!!!(这里的公式已经是精简版了)
最后一句忠告,写博客多按保存(富文本编辑器似乎会过一会就自动保存一下,不过不能写数学公式),写代码也是…

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\n", solve(n));
	}
}

参见 传送门:大佬博客









再次吐槽数学公式的难打

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!