码迷,mamicode.com
首页 > 其他好文 > 详细

HDU-5608(杜教筛)

时间:2020-06-01 20:47:19      阅读:44      评论:0      收藏:0      [点我收藏+]

标签:math   ase   int   rac   pre   not   span   快速   for   

HDU-5608(杜教筛)

题意:\(G(n)=n^2?3n+2=\sum_{d|n}F(d)\),求\(\sum_1^nF(i)\)

反演得到:\(F(n)=\sum_{d|n}\mu(d)G(\frac{n}{d})\)

\(\sum_1^nF(i)=\sum_i\sum_{d|i}\mu(d)G(\frac{i}{d})\)

\(\sum_1^nF(i)=\sum_{i=1}^{n}G(i)\sum_{j=1}^{\lfloor \frac{n}{i}\rfloor }\mu(j)\)

问题就是要快速求\(G(i)\)前缀和和\(\mu(i)\)前缀和

第一个是\(O(1)\)求,第二个是杜教筛


const int N=5e6+10,P=1e9+7;

ll qpow(ll x,ll k=P-2) {
	ll res=1;
	for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
	return res;
}

const int Inv6=qpow(6);

int n;
char notpri[N],w[N];
int pri[N/4],pc,Sw[N];
map <int,int> M;

int SumG(int n){ // O(1)求出G函数的前缀和
	ll ans=1ll*n*(n+1)%P*(2*n+1)%P*Inv6%P;
	ans=(ans-3ll*n*(n+1)/2%P+2*n)%P;
	ans=(ans%P+P)%P;
	return ans;
}

int Sumw(int n){ // 杜教筛求mobius函数前缀和
	if(n<N) return Sw[n];
	if(M.count(n)) return M[n];
	int ans=1;
	for(int i=2,j;i<=n;i=j+1) {
		j=n/(n/i);
		ans-=(j-i+1)*Sumw(n/i);
	}

	return M[n]=ans;
}

int SumF(int n){ // 答案
	int ans=0;
	for(int i=1,j;i<=n;i=j+1) {
		j=n/(n/i);
		ans=(ans+1ll*(SumG(j)-SumG(i-1))*Sumw(n/i))%P;
	}
	ans=(ans%P+P)%P;
	return ans;
}

int main(){
	w[1]=1;
	rep(i,2,N-1) {
		if(!notpri[i]) pri[++pc]=i,w[i]=-1;
		for(int j=1;j<=pc && 1ll*i*pri[j]<N;++j) {
			notpri[i*pri[j]]=1;
			if(i%pri[j]==0) {
				w[i*pri[j]]=0;
				break;
			}
			w[i*pri[j]]=-w[i];
		}
	}
	rep(i,1,N-1) Sw[i]=Sw[i-1]+w[i];
	rep(kase,1,rd()) printf("%d\n",SumF(rd()));
}




HDU-5608(杜教筛)

标签:math   ase   int   rac   pre   not   span   快速   for   

原文地址:https://www.cnblogs.com/chasedeath/p/13026879.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!