筛子

分块前缀和杜教筛很详细了……

PPT: D:/shr/OI/数论.pdf (雾

powerful number

考虑求一个奇怪积性函数 \(f(x)\) 的前缀和 \(F(x)\)

然后想到找一个前缀和好算的积性函数 \(g(x)\) ,使得任意 \(p\) 为素数, \(g(x)=f(x)\)

求出积性函数函数 \(h\) 使 \(f=g*h\) ,先求出素数次幂 \(p^k\)\(h\) ,每次复杂度为 \(O(k^2)\)

再考虑一下 \(h\) 的性质:

当求质数点值时: \(f(p)=g(p)h(1)+g(1)h(p)\)

由积性函数 \(1\) 处值为 \(1\) 所以 \(h(p)=0\)

再由积性函数性质,所有满足有素因子指数为 \(1\)\(x\)\(h(x)=0\)

剩下所有 \(h(x)\not=0\)\(x\) 都称作 \(\text{powerful number}\)

这样的数均可以表示为 \(a^3b^2\) 的形式,有 \(O(\sqrt{n})\) 个。

可以爆搜求出所有 \(\text{powerful number}\) 及其点值。

\(F(n)=\sum_{d=1}^{n}h(i)G(\frac{n}{d})\) ,对于所有 \(\text{powerful number}\) 处求值即可。

min_25 筛

求积性函数 \(f(i)\) 的前缀和。我们显然要知道且能够快速算出 \(f(p^k)\) 的值。

先把 \(f(i)\) 手动拆成 \(O(1)\) 个完全积性函数的和或差,后面要用到性质。

\(P_j\) 为第 \(j\) 个质数,对每个完全积性函数 \(f'(i)\) 分别求一个 \(g(n,j)=[p\in\mathbb{P}|\min_{i|p}i>P_j]f'(p)\)

本质上是用前 \(j\) 个质数埃氏筛剩下的数 \(f'\) 之和。

DP 求出 \(g\) 的值:

\(P_j^2>n\) 时: \(g(n,j)=g(n,j-1)\) ,在 \(j\) 之前所有数都筛完了。

\(P^j\le n\) 时: \(g(n,j)=g(n,j-1)-f'(P_j)(g(\frac{n}{P_j},j-1)-\sum_{i=1}^{j-1}f'(P_i))\)

因为 \(f'\) 是个完全积性函数,所以是对的。

有注意到只需要对 \(P_j\ge\sqrt{n}\)\(j\) 进行转移,且 \(\frac{n}{P_j}\) 只会有 \(O(\sqrt{n})\) 个取值,所以这里复杂度还好。

(需要离散化一下 \(\frac{n}{P_j}\) ,注意 DP 的时候对于每个 \(n\) 只需要一个变量,能直接从 \(j-1\) 继承, 保证 \(\frac{n}{P_j}\) 会比 \(n\) 后更新,即从大到小做即可)

此时已经求出了一个东西:\(g(n,|\mathbb{P}|)\)\(f'\) 在质数处的前缀和。

考虑求出所有数的前缀和,设 \(s(n,j)=[\min_{i|p}i>P_j]f(p)\) ,注意此时求的是积性函数 \(f\) 的了,不是 \(f'\) 的。

答案就是 \(s(n,0)\)

这样设,按照 \(s\) 的定义,可以满足积性函数的性质来进行转移(这之后只会从 \(j\) 更大的 \(S\) 来转移过来,不会和因子 \(P_j\) 有重叠)。

\[s(n,j)=\sum g(n,|\mathbb{P}|)-\sum_{i=1}^{j}\sum f'(P_i)+\sum_{k>j}\sum_{e=1}^{P_k^e\le n}f(P_k^e)(s(\frac{n}{P_k^e},k)+[e>1]) \]

意为所有质数减去 \(P_j\) 以内质数的贡献,加上合数的贡献。

\([e>1]\) 是因为当 \(e=1\) 时,为质数,在最前面已经加上贡献了。

同样只需要 \(P_j\le\sqrt{n}\) 的,且 \(n\) 只用 \(O(\sqrt{n})\) 个取值。

不过注意到上面所有讨论都对 \(1\) 是无效的,所以前面应减掉 \(1\) 的贡献,最后答案为 \(s(n,0)+1\)

Luogu5325 代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+7,mod=1e9+7;
ll n,c[N];
int m,sz,s,b[N],p[N],s1[N],s2[N],g1[N],g2[N],id1[N],id2[N];
int gt(ll x) {return x<=m?id1[x]:id2[n/x];}
ll f1(ll x) {x%=mod;return x*(x+1)/2%mod;}
ll f2(ll x) {x%=mod;return x*(x+1)%mod*(x*2+1)%mod*166666668%mod;}
int solve(ll x,int j){
	if(p[j]>x) return 0;
	int res=((g2[gt(x)]-g1[gt(x)]-s2[j]+s1[j])%mod+mod)%mod;
	for(int i=j+1;i<=sz&&1ll*p[i]*p[i]<=x;i++)
		for(ll e=1,ml=p[i];ml<=x;ml*=p[i],e++) (res+=ml%mod*(ml%mod-1)%mod*(solve(x/ml,i)+(e>1))%mod)%=mod;
	return res;
}
signed main(){
	scanf("%lld",&n);m=sqrt(n);
	for(int i=2;i<=m;i++){
		if(!b[i]) p[++sz]=i;
		for(int j=1;i*p[j]<=m&&j<=sz;j++){
			b[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
	for(int i=1;i<=sz;i++) s1[i]=(s1[i-1]+p[i])%mod,s2[i]=(s2[i-1]+1ll*p[i]*p[i]%mod)%mod;
	for(ll l=1,r;l<=n;l=r+1){
		r=n/(c[++s]=n/l);
		g1[s]=f1(c[s])-1;g2[s]=f2(c[s])-1;
		if(c[s]<=m) id1[c[s]]=s;else id2[n/c[s]]=s;
	}
	for(int i=1;i<=sz;i++)
		for(int j=1;j<=s&&1ll*p[i]*p[i]<=c[j];j++){
			g1[j]=(g1[j]-1ll*p[i]*(g1[gt(c[j]/p[i])]-s1[i-1])%mod+mod)%mod;
			g2[j]=(g2[j]-1ll*p[i]*p[i]%mod*(g2[gt(c[j]/p[i])]-s2[i-1])%mod+mod)%mod;
		}
	printf("%d\n",(solve(n,0)+1)%mod);
	return 0;
} 

为啥我一会叫素数一会叫质数!!!

posted @ 2021-07-29 10:05  shrtcl  阅读(65)  评论(0)    收藏  举报