【题解】Loj6053 简单的函数

Link

\(\text{Solution:}\)

显然的 Min_25 筛。因为题目已经告诉我们:函数是积性函数,并且素数及其次幂处的点值可以快速计算。

先把给的函数拆成若干完全积性函数的和:我们观察到,当 \(p\) 为质数的时候, \(f(p)=p-1.\) 所以我们可以把函数拆成 \(f(p)=p,f(p)=1\) 这两个来分别计算出 \(g_1(n,j),g_2(n,j)\) 数组。

但是对于 \(2\) 这个性质不成立怎么办?考虑计算答案的时候去掉影响就可以了,先按照 \(1\) 算即可。

于是线性筛的时候我们需要维护一下素数的和,至于 \(1\) 这一部分自己算也行,为了板子化也可以求一下前缀和。

同样地,记录下基本和组,并把所有数都当成质数来计算 \(g,\) 因为最后用到的只有素数的部分。

考虑合并答案,实际上就是一样的式子:不断提出 \(p[i]\) 直到把因子提干净,一步步计算即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=2e5+10;
inline int Add(int x,int y) {
	return (x+y)%mod;
}
inline int Mul(int x,int y) {
	return 1ll*x*y%mod;
}
inline int dec(int x,int y) {
	return (x-y+mod)%mod;
}
bool vis[N];
int p[N],sum1[N],cnt,sq,sp,id1[N],id2[N],g1[N],n,g2[N],w[N],m,sum2[N];
void pre() {
	for(int i=2; i<=sq; ++i) {
		if(!vis[i])p[++cnt]=i;
		for(int j=1; j<=cnt&&i*p[j]<=sq; ++j) {
			vis[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
	for(int i=1; i<=cnt; ++i) {
		sum2[i]=Add(sum2[i-1],p[i]);
		sum1[i]=sum1[i-1]+1,sum1[i]%=mod;
	}
}
inline int f2(int x) {
	x%=mod;
	return x*(x+1)/2%mod;
}
inline int getid(int x) {
	if(x<=sq)return id1[x];
	return id2[n/x];
}
inline int S(int x,int j) {
	if(p[j]>x)return 0;
	int Ans=dec(dec(g2[getid(x)],g1[getid(x)]),dec(sum2[j],sum1[j]));
	if(j==0&&x>=2)Ans=Add(Ans,2);
	for(int i=j+1; i<=cnt&&p[i]*p[i]<=x; ++i) {
		for(int e=1,sp=p[i]; sp<=x; sp*=p[i],++e) {
			Ans=Add(Ans,Mul((p[i]^e)%mod,Add(S(x/sp,i),(e>1))));
		}
	}
	return Ans;
}
signed main() {
	scanf("%lld",&n);
	sq=sqrt(n);
	pre();
	for(int l=1,r; l<=n; l=r+1) {
		int d=n/l;
		r=n/d;
		w[++m]=d;
		g1[m]=(w[m]-1+mod)%mod;
		g2[m]=f2(w[m])-1;
		g2[m]+=mod;g2[m]%=mod;
		if(w[m]<=sq)id1[w[m]]=m;
		else id2[r]=m;
	}
	for(int i=1; i<=cnt; ++i) {
		for(int j=1; j<=m&&p[i]*p[i]<=w[j]; ++j) {
			g1[j]=dec(g1[j],dec(g1[getid(w[j]/p[i])],sum1[i-1]));
			g2[j]=dec(g2[j],Mul(p[i],dec(g2[getid(w[j]/p[i])],sum2[i-1])));
		}
	}
	printf("%lld\n",(S(n,0)+mod+1)%mod);
	return 0;
}
posted @ 2021-07-30 20:42  Refined_heart  阅读(82)  评论(0编辑  收藏  举报