某考试 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\n",query(n)); } return 0; }
我爱学习,学习使我快乐