SP20174 DIVCNT3 - Counting Divisors (cube)

· · 题解

前置知识:Min_25 筛

默认读者已经掌握 Min_25 筛。

思路

f(n) = \sigma(n^{\textcolor{C74541}{3}})。显然 f 为积性函数。

考虑 Min_25 筛。

  1. 对于质数 pf(p) = \textcolor{C74541}{4},符合 f(p) 是可快速求值的完全积性函数的要求。

  2. 对于质数 pf(p^c) = \textcolor{C74541}{3}c + 1,符合 f(p^c) 可快速求值的要求。

g(p) = 1,则 f(p) = \textcolor{C74541}{4} g(p)

然后就可以用 Min_25 筛做了。

首先预处理出前 \lfloor \sqrt{n} \rfloor 个质数与它们对应的 G_{\text{prime}} 值。

g(p) = 1,有 G_{\text{prime}}(p_i) = G_{\text{prime}}(p_{i-1}) + 1

然后用一次整除分块找到所有有效点 n'

显然 G_0(n') = n' - 1

对于所有 n',从 G_0(n') 递推到 G_{\ell(n')}(n'),即得 G_\text{prime}(n')

$$\begin{aligned} G_k(n) &= G_{k-1}(n) - g(p_k)\left( G_{k-1}(n/p_k) - G_{\text{prime}}(p_{k-1}) \right)\\ &= G_{k-1}(n) - \left( G_{k-1}(n/p_k) - G_{\text{prime}}(p_{k-1}) \right). \end{aligned}$$ 最后计算 $F_1(n)$。 $F_k(n)$ 的递推式为: $$\begin{aligned} F_k(n) &= F_{\text{prime}}(n) - F_{\text{prime}}(p_{k-1}) + \sum\limits_{\substack{i \ge k\\p_i^2 \le n}}\sum\limits_{\substack{c \ge 1\\p_i^{c+1} \le n}}\left( f(p_i^{c+1}) + f(p_i^c)F_{i+1}(n/p_i^c) \right)\\ &= \textcolor{C74541}{4} \left( G_{\text{prime}}(n) - G_{\text{prime}}(p_{k-1}) \right) + \sum\limits_{\substack{i \ge k\\p_i^2 \le n}}\sum\limits_{\substack{c \ge 1\\p_i^{c+1} \le n}}\left( \textcolor{C74541}{3}(c+1) + 1 + (\textcolor{C74541}{3}c + 1)F_{i+1}(n/p_i^c) \right). \end{aligned}$$ 递归计算即可。 $F_1(n) + 1$ 即为所求的 $S_3(n)$。 时间复杂度为 $\Theta(n^{1 - \epsilon})$。 ## Code ```cpp line-numbers #include<bits/stdc++.h> #define int long long const int B=3.2e5+10; using namespace std; int T; int n,b,ans; int cnt,p[B]; bool vis[B]; int s[B]; int tot,w[B<<1]; int id1[B],id2[B]; int g[B<<1]; inline int read(){ int ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+(ch&15),ch=getchar(); return ret*f; } int _sqrt(int x){ int ret=floor(sqrtl(x)); while((ret+1)*(ret+1)<=x)ret++; return ret; } int get_id(int _n){ if(_n<=b)return id1[_n]; return id2[n/_n]; } void _init(){ cnt=tot=0; for(int i=1;i<=b;i++)p[i]=vis[i]=s[i]=id1[i]=id2[i]=0; for(int i=1;i<=b*2;i++)w[i]=g[i]=0; for(int i=2;i<=b;i++){ if(!vis[i]){ p[++cnt]=i; s[cnt]=s[cnt-1]+1; } for(int j=1;j<=cnt&&i*p[j]<=b;j++){ vis[i*p[j]]=1; if(!(i%p[j]))break; } } for(int l=1,r;l<=n;l=r+1){ r=n/(n/l); int _n=n/l; w[++tot]=_n; if(_n<=b)id1[_n]=tot; else id2[n/_n]=tot; g[tot]=_n-1; } for(int k=1;k<=cnt;k++){ for(int i=1;i<=tot&&p[k]*p[k]<=w[i];i++){ int id=get_id(w[i]/p[k]); g[i]-=g[id]-s[k-1]; } } } int F(int k,int _n){ if(_n<p[k])return 0; int id=get_id(_n); int ret=(g[id]-s[k-1])*4; for(int i=k;i<=cnt&&p[i]*p[i]<=_n;i++){ for(int c=1,_p=p[i];_p*p[i]<=_n;c++,_p*=p[i]){ ret+=((c+1)*3+1)+(c*3+1)*F(i+1,_n/_p); } } return ret; } void _solve(){ n=read(); b=_sqrt(n); _init(); ans=F(1,n)+1; printf("%lld\n",ans); } signed main(){ T=read(); while(T--)_solve(); return 0; } ``` ## 后记 双倍经验:**[DIVCNTK](https://www.luogu.com.cn/article/63nfpshh)**。 事实上 **[DIVCNT2](https://www.luogu.com.cn/article/87fdh0gf)** 也可以用 Min_25 筛做,但是跑得比较慢。