我是靠谱客的博主 温暖豆芽,最近开发中收集的这篇文章主要介绍[SPOJ] DIVCNT2 - Counting Divisors (square) 积性函数前缀和,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

题面
题目要我们求

i=1nσ0(i2)

i 分解质因数pα11pα22...pαkk
就是求
i=1nj=1k(2αj+1)

考虑每个乘式选 2αj 还是 1 ,枚举集合就是
i=1nS{1,2,...,k}2|S|jαj

S 集合看成选αj>0的,把 jαj 看成枚举每个 >0 αj ,事实上就是枚举 i 的约数,每个约数的贡献是2ω(d),其中 ω(i) 表示 i 的质因数个数,所以就是
i=1nd|n2ω(d)

其实把 ω(d) 看成每个质因数选或不选,就是 d 的无平方因子约数的个数,所以式子变成
i=1nd|np|dμ2(p)

改成枚举 μ2(i) ,就是
i=1nμ2(i)j=1niσ0(j)

于是利用整除分块,我们只要知道 μ2(i) σ0(i) 的前缀和,按照杜教筛的复杂度分析,就能在预处理前 n23 项的情况下, O(n23) 求出。
前者就是 1 n 的无平方因子数个数,考虑容斥 O(n)
i=1nμ2(i)=i=1nμ(i)ni2

后者就是sb套路,整除分块 O(n)
i=1nσ0(i)=i=1nni

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=50000000;
ll n;
int pri[10000000],num,mu[maxn+5],d[maxn+5],sm[maxn+5],sd[maxn+5],ci[maxn+5];
bool flag[maxn+5];
void getpri(int n)
{
memset(flag,1,sizeof(flag));
flag[1]=0;mu[1]=1;d[1]=1;
for(int i=2;i<=n;i++)
{
if(flag[i]) pri[++num]=i,mu[i]=-1,ci[i]=1,d[i]=2;
for(int j=1;j<=num&&i*pri[j]<=n;j++)
{
int v=i*pri[j];
flag[v]=0;ci[v]=1;
if(i%pri[j]==0) {ci[v]+=ci[i];mu[v]=0;d[v]=d[i]*(ci[v]+1)/ci[v];break;}
mu[v]=-mu[i];d[v]=(d[i]<<1);
}
}
}
ll qsd(ll m)
{
if(m<=maxn) return sd[m];
ll re=0;
for(ll l=1,r=1,k;l<=m;l=r+1)
{
k=m/l;r=m/k;
re+=(ll)(r-l+1)*k;
}
return re;
}
ll qsm(ll m)
{
if(m<=maxn) return sm[m];
ll gen=round(sqrt(m)+1),re=0;
for(ll i=1;i<=gen;i++)
re+=mu[i]*(m/(i*i));
return re;
}
ll solve()
{
ll re=0;
for(ll l=1,r=1,k;l<=n;l=r+1)
{
k=n/l;r=n/k;
re+=qsd(k)*(qsm(r)-qsm(l-1));
}
return re;
}
int main()
{
int ca,lim=maxn;
scanf("%d",&ca);
if(ca>800) lim=10000;
getpri(lim);
for(int i=1;i<=lim;i++)
sm[i]=sm[i-1]+mu[i]*mu[i],sd[i]=sd[i-1]+d[i];
while(ca--)
{
scanf("%lld",&n);
printf("%lldn",solve());
}
return 0;
}

最后

以上就是温暖豆芽为你收集整理的[SPOJ] DIVCNT2 - Counting Divisors (square) 积性函数前缀和的全部内容,希望文章能够帮你解决[SPOJ] DIVCNT2 - Counting Divisors (square) 积性函数前缀和所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部