某考试 T1 function

(数据范围 n<=10^9 ,T<=10 )

首先,我来证明一下 Σμ(d) * σ(i/d)^2 = σ(i^2)  

相信做过约数个数和的童鞋都可以完成从右式推到左式,那么我现在就说一下怎么从左边推到右边。

     Σμ(d) * σ(i/d)^2

= Σμ(d) * Σ(d|p|i) * Σ(d|q|i)

= Σ(p|i) * Σ(q|i) * Σ(d|p,d|q) μ(d)

= Σ(p|i) * Σ(q|i) * [gcd(p,q)==1]

= σ(i^2)

然后这个题就变成了昨天晚上我做的那个题。

但是我突然又想再推一遍式子。。。

    σ(i^2) 

= (2*a1 + 1)(2*a2 + 1)(2*a3 + 1)...(2*ak + 1)

= Σ(S属于{1,2,...k}) 2^|S|  *  π(i属于S) ai

= Σ (p|i) 2^w(p)

其中w(x)表示x的质因子个数。

又因为 2^w(x) = Σ (d|x) μ(d)^2

所以上式的前缀和就可以转化成μ^2和σ的前缀和之间的运算了

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1000000;
int zs[maxn/5],t=0,T,sq[maxn+5];
int miu[maxn+5],low[maxn+5],n;
bool v[maxn+5];
ll d[maxn+5];

inline void init(){
	miu[1]=1,d[1]=1,low[1]=1;
	for(int i=2;i<=maxn;i++){
		if(!v[i]) zs[++t]=i,miu[i]=-1,d[i]=2,low[i]=i;
		for(int j=1,u;j<=t&&(u=zs[j]*i)<=maxn;j++){
			v[u]=1;
			if(!(i%zs[j])){
				low[u]=low[i]*zs[j];
				if(low[i]==i) d[u]=d[i]+1;
				else d[u]=d[low[u]]*d[i/low[i]];
				break;
			}
			
			low[u]=zs[j];
			d[u]=d[i]<<1;
			miu[u]=-miu[i];
		}
	}
	
	for(int i=1;i<=maxn;i++) d[i]+=d[i-1];
	for(int i=1;i<=maxn;i++) sq[i]=sq[i-1]+miu[i]*miu[i];
}

inline int getsq(int x){
	if(x<=maxn) return sq[x];
	
	ll an=0;
	for(int i=1;i*(ll)i<=x;i++){
		an+=miu[i]*(x/(i*(ll)i));
	}
	return an;
}

inline ll getd(int x){
	if(x<=maxn) return d[x];
	
	ll an=0;
	for(int i=1,j,now;i<=x;i=j+1){
		now=x/i,j=x/now;
		an+=(j-i+1)*(ll)now;
	}
	return an;
}

inline ll query(int x){
	ll an=0;
	for(int i=1,j,now;i<=x;i=j+1){
		now=x/i,j=x/now;
		an+=(getsq(j)-getsq(i-1))*getd(now);
	}
	return an;
}

int main(){
	freopen("function.in","r",stdin);
	freopen("function.out","w",stdout);
	
	scanf("%d",&T);
	init();
	while(T--){
		scanf("%d",&n);
		printf("%lld
",query(n));
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/JYYHH/p/8512396.html