• SP20173 题解


    题解

    提供一个贝尔级数爆拆 \(\sigma_0(n^2)\) 的做法(贝尔级数可见 command_block 的博客 Part. 2)。

    设函数 \(f(n)=\sigma_0(n^2)\),不难证明这是一个积性函数:

    对于 \(p\bot q\)\(f(p)f(q)=\sigma_0(p^2)\sigma_0(q^2)=\sigma_0((pq)^2)=f(pq)\),且 \(f(1)=1\)

    对于积性函数,就可以写出其贝尔级数:

    \[\begin{array}{c} f(1)=1\\ f(p)=\sigma_0(p^2)=3\\ f(p^2)=\sigma_0(p^4)=5\\ \vdots\\ f(p^k)=\sigma_0(p^{2k})=2k+1\\ \vdots \end{array} \]

    不难看出是一个等差数列。

    \(f'(x)\) 为积性函数 \(f(x)\) 的贝尔级数。

    \[f'(x)=\sum_{k=0}^{\infty}(2k+1)x^{k} \]

    用生成函数基本操作:

    \[\begin{aligned} f'(x)&=\sum_{k=0}^\infty(2k+1)x^k\\ f'(x)x&=\sum_{k=1}^\infty (2k-1)x^k\\ f'(x)(1-x)&=1+\sum_{k=1}^\infty 2x^k=g(x)\\ g(x)-g(x)x&=1+x\\ g(x)&=\frac{1+x}{1-x}=f'(x)(1-x)\\ f'(x)&=\frac{1+x}{(1-x)^2} \end{aligned} \]

    众所周知,\(\mu^2(x)\) 的贝尔级数为 \(1+x\)\(1(x)\) 的贝尔级数为 \(\frac{1}{1-x}\)

    这里的 \(1(x)\)\(\mu^{-1}(x)\),即 \((1*\mu)(x)=\epsilon(x)\),其中 \(*\) 表示迪利克雷卷积运算。

    于是,\(f(x)=\mu^2*1*1\)。记 \(h(x)=\mu^2*1\)

    \[\begin{aligned} \sum_{T=1}^nf(T)&=\sum_{T=1}^n\sum_{d\mid T}h(d)\\ &=\sum_{d=1}^nh(d)\lfloor\frac{n}{d}\rfloor \end{aligned} \]

    对这个东西数论分块搞一搞,求 \(h(n)\) 的前缀和即可。

    于是开始疯狂套用杜教筛:

    \[\begin{aligned} \sum_{T=1}^nh(T)&=\sum_{T=1}^n\sum_{d\mid T}\mu^2(d)\\ &=\sum_{d=1}^n\mu^2(d)\lfloor\frac{n}{d}\rfloor \end{aligned} \]

    快速求 \(\mu^2(n)\) 的前缀和即可(此步快速求 \(\mu^2(n)\) 前缀和可见)。

    \[\begin{aligned} \sum_{T=1}^n\mu^2(T)&=\sum_{T=1}^n\sum_{d^2\mid T}\mu(d)\\ &=\sum_{d=1}^{\sqrt{n}}\mu(d)\lfloor\frac{n}{d^2}\rfloor \end{aligned} \]

    关于时间复杂度:

    image-20220316193431780.png

    预处理处 \(f(n)\)\(h(n)\)\(\mu^2(n)\)\(\mu(n)\) 的前 \(n^{\frac{2}{3}}\) 项的前缀和,加上杜教筛即可,时间复杂度 \(\mathcal O(n^\frac{2}{3})\)

    关于 \(h(n)\) 的线性筛:

    \[\begin{aligned} h(p^k) &= (\mu^2*1)(p^k)\\ &= \sum_{i=0}^k\mu^2(p^i)\\ &=\sum_{i=0}^{\min(1, k)}\mu^2(p^i)\\ &=\min(2,k+1) \end{aligned} \]

    关于 \(f(n)\) 的线性筛:

    \[\begin{aligned} f(p^k) &= (\mu^2*1*1)(p^k)\\ &= (\mu^2*\sigma_0)(p^k)\\ &= \sum_{i=0}^{\min(1,k)}\sigma_0(p^i) \end{aligned} \]

    关于 \(\sigma_0(p^k)\),显然有 \(\sigma_0(p^k)=k+1\)

    关于线性筛,在筛的时候若出现 i%prime[j] == 0 的情况,i * prime[j] 的最小质因数幂次必然大于等于 \(2\),可以省去上面 \(\sum\) 中的 \(\min\)。详见代码中 if(i%pr[j] == 0) 部分。

    最后注意一下第一个点的时限为 1s,如果仍进行大规模预处理可能会 TLE。

    参考实现

    // Problem: SP20173 DIVCNT2 - Counting Divisors (square)
    // From: Luogu
    // URL: https://www.luogu.com.cn/problem/SP20173
    // Time: 2022-03-16 19:39
    // Author: lingfunny
    
    #include <bits/stdc++.h>
    #include<ext/pb_ds/assoc_container.hpp>
    #include<ext/pb_ds/hash_policy.hpp>
    #define LL long long
    #define UL unsigned long long
    using namespace std;
    using namespace __gnu_pbds;
    const int mxz = 7.7e7+100;
    unsigned int mu2[mxz];
    UL h[mxz], f[mxz];
    int pr[(int)1e7], tot, mu[mxz], t, mxn;
    LL ask[(int)1e7];
    short flg[mxz];
    inline int mi(int p, short c) {
    	int res = 1;
    	while(c) { if(c&1) res *= p; p *= p; c >>= 1; }
    	return res;
    }
    inline void Get() {
    	mu[1] = f[1] = h[1] = mu2[1] = 1;
    	for(int i = 2; i < mxn; ++i) {
    		if(!flg[i]) pr[++tot] = i, mu[i] = -1, flg[i] = 1, h[i] = 2, f[i] = 3;
    		for(int j = 1; j <= tot && pr[j] * i < mxn; ++j) {
    			int v = i * pr[j];
    			if(i%pr[j] == 0) {
    				flg[v] = flg[i] + 1;
    				// 最小质因数幂次至少为 2
    				h[v] = h[v/mi(pr[j], flg[v])]<<1;
    				f[v] = f[v/mi(pr[j], flg[v])] * (flg[v]+flg[v]+1);
    				break;
    			}
    			flg[v] = 1;
    			mu[v] = -mu[i];
    			h[v] = h[i] << 1;
    			f[v] = f[i] * 3;
    		}
    	}
    	for(int i = 1; i < mxn; ++i) {
    		mu2[i] = mu[i] * mu[i];
    		mu2[i] += mu2[i-1];
    		h[i] += h[i-1];
    		f[i] += f[i-1];
    	}
    }
    gp_hash_table <LL, UL> qh, qf, qm;
    inline UL Qm(LL n) {
    	if(n < mxn) return mu2[n];
    	if(qm.find(n) != qm.end()) return qm[n];
    	UL res = 0;
    	for(int d = 1; (LL)d*d <= n; ++d)
    	res += mu[d] * (n/((LL)d*d));
    	qm[n] = res;
    	return res;
    }
    inline UL Qh(LL n) {
    	if(n < mxn) return h[n];
    	if(qh.find(n) != qh.end()) return qh[n];
    	register UL res = 0, pre = 0, cur = 0;
    	for(register LL l = 1, r; l <= n; l = r + 1) {
    		r = n/(n/l);
    		cur = Qm(r);
    		res += (cur-pre) * (n/l);
    		pre = cur;
    	}
    	qh[n] = res;
    	return res;
    }
    inline UL Qf(LL n) {
    	if(n < mxn) return f[n];
    	if(qf.find(n) != qf.end()) return qf[n];
    	register UL res = 0, pre = 0, cur = 0;
    	for(register LL l = 1, r; l <= n; l = r + 1) {
    		r = n/(n/l);
    		cur = Qh(r);
    		res += (cur-pre) * (n/l);
    		pre = cur;
    	}
    	qf[n] = res;
    	return res;
    }
    
    signed main() {
    	Get();
    	scanf("%d", &t);
    	LL n = 0;
    	for(int i = 1; i <= t; ++i) scanf("%lld", &ask[i]), n = max(n, ask[i]);
    	if(n <= 1e4+10) mxn = 1e4+2;
    	else mxn = mxz-100;
    	Get();
    	for(int i = 1; i <= t; ++i) printf("%llu\n", Qf(ask[i]));
    	return 0;
    }
    
  • 相关阅读:
    软件工程(2018)第一次作业
    SQA计划
    再冲刺
    第三次冲刺
    第二次冲刺
    小组第一次冲刺
    团队合作初体验
    关于git的认识与想法
    我的第一篇博客
    SQA计划和系统测试规程
  • 原文地址:https://www.cnblogs.com/lingfunny/p/16361136.html
Copyright © 2020-2023  润新知