我是靠谱客的博主 顺心中心,这篇文章主要介绍SPOJ DIVCNT2 - Counting Divisors (square),现在分享给大家,希望可以做个参考。

题目描述:

∑ i = 1 n σ 0 ( i 2 ) sum_{i=1}^nsigma_0(i^2) i=1nσ0(i2)
其中 σ 0 sigma_0 σ0表示约数个数, n ≤ 1 0 12 nle10^{12} n1012,时间限制20s

题目分析:

σ 0 ( i ) = ∑ d ∣ i σ 0 ( i 2 ) = ∑ d ∣ i 2 ω ( d ) largesigma_0(i)=sum_{d|i}\sigma_0(i^2)=sum_{d|i}2^{omega(d)} σ0(i)=diσ0(i2)=di2ω(d)
其中, ω ( d ) omega(d) ω(d)表示 d d d的质因子个数,上式可以理解为从i中选出一些质因子,每个质因子可以取当前次幂或者加上最高次幂两种选择。

可看出 2 ω ( d ) 2^{omega(d)} 2ω(d)为d的无平方因子的约数个数,所以
2 ω ( d ) = ∑ k ∣ d ∣ μ ( k ) ∣ large2^{omega(d)}=sum_{k|d}|mu(k)| 2ω(d)=kdμ(k)
整理一下,记 g ( i ) = ∣ μ ( k ) ∣ g(i)=|mu(k)| g(i)=μ(k),那么 σ 0 ( i 2 ) = ( g ∗ I ) ∗ I = g ∗ ( I ∗ I ) = g ∗ σ 0 sigma_0(i^2)=(g*I)*I=g*(I*I)=g*sigma_0 σ0(i2)=(gI)I=g(II)=gσ0
所以 ∑ i = 1 n σ 0 ( i 2 ) = ∑ i = 1 n ∑ d ∣ i ∣ μ ( i d ) ∣ ∗ σ 0 ( d ) = ∑ i = 1 n ∣ μ ( i ) ∣ ∑ d = 1 n i σ 0 ( d ) sum_{i=1}^nsigma_0(i^2)=sum_{i=1}^nsum_{d|i}|mu(frac id)|*sigma_0(d) \=sum_{i=1}^n|mu(i)|sum_{d=1}^{frac ni}sigma_0(d) i=1nσ0(i2)=i=1ndiμ(di)σ0(d)=i=1nμ(i)d=1inσ0(d)
显然需要处理 ∣ μ ( i ) ∣ |mu(i)| μ(i) σ 0 ( i ) sigma_0(i) σ0(i)的前缀和
线性筛五千万到一亿(少了会TLE),剩下的可以这么算:

  • 从" n n n以内无平方因子数的个数"的意义出发,考虑容斥,可以得出:
    ∑ i = 1 n ∣ μ ( i ) ∣ = ∑ i = 1 n μ ( i ) ∗ ⌊ n i 2 ⌋ sum_{i=1}^n|mu(i)|=sum_{i=1}^{sqrt n}mu(i)*leftlfloorfrac n{i^2}rightrfloor i=1nμ(i)=i=1n μ(i)i2n
    O ( n ) O(sqrt n) O(n )计算
  • 约数个数的前缀和很好算:
    ∑ i = 1 n σ 0 ( i ) = ∑ i = 1 n ⌊ n i ⌋ sum_{i=1}^nsigma_0(i)=sum_{i=1}^nleftlfloorfrac nirightrfloor i=1nσ0(i)=i=1nin
    O ( n ) O(sqrt n) O(n )分块计算

与杜教筛的时间复杂度分析类似,总时间 O ( n 2 3 ) large O(n^{frac 23}) O(n32)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<cstdio> #include<algorithm> #define LL long long using namespace std; const int N = 50000000; int p[N/10],mu[N+5],sd[N+5],sm[N+5]; bool v[N+5]; void Prime(int N) { mu[1]=sd[1]=sm[1]=1;int cnt=0; for(int i=2;i<=N;i++) { if(!v[i]) p[++cnt]=i,mu[i]=-1,sm[i]=sd[i]=2;//sm now replace ak to use for(int j=1,k;j<=cnt&&p[j]*i<=N;j++) { v[k=p[j]*i]=1; if(i%p[j]==0) {mu[k]=0,sd[k]=sd[i]/sm[i]*(sm[i]+1),sm[k]=sm[i]+1;break;} mu[k]=-mu[i],sd[k]=sd[i]*sd[p[j]],sm[k]=2; } } for(int i=1;i<=N;i++) sd[i]+=sd[i-1],sm[i]=sm[i-1]+abs(mu[i]); } LL Summu(LL n) { if(n<=N) return sm[n]; LL ret=0; for(LL i=1;i*i<=n;i++) ret+=mu[i]*n/(i*i); return ret; } LL Sumd(LL n) { if(n<=N) return sd[n]; LL ret=0; for(LL i=1,j;i<=n;i=j+1) { j=n/(n/i); ret+=(j-i+1)*(n/i); } return ret; } LL solve(LL n) { LL ans=0; for(LL i=1,j,pre=0,tmp;i<=n;i=j+1) { j=n/(n/i); tmp=Summu(j); ans=ans+Sumd(n/i)*(tmp-pre); pre=tmp; } return ans; } int main() { LL n[10005];int T; scanf("%d",&T); for(int i=1;i<=T;i++) scanf("%lld",&n[i]); if(*max_element(n+1,n+1+T)<=10000) Prime(10000); else Prime(N); for(int i=1;i<=T;i++) printf("%lldn",solve(n[i])); }

最后

以上就是顺心中心最近收集整理的关于SPOJ DIVCNT2 - Counting Divisors (square)的全部内容,更多相关SPOJ内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部