BZOJ 5244 [Fjwc2018]最大真因數(Min_25篩變形 + 積性)
所謂最大真因數,就是不包括他自己的最大因子。顯然質數的最大真因子為1,而合數的最大真因子是他本身除以最小質因子。
而本題要求求所有的合數的最大真因子之和。然後資料範圍是5e9,一眼看上去就是一個Min_25篩嘛,求出字首和s,然後減去質數部分和g即可。然後啪啪啪把板子敲上去,然後跑了跑樣例發現根本過不了……然後開始懷疑人生,後來仔細一想,原來最大真因子這個東西,他不滿足積性,也就不能用Min_25篩直接去求它的和,也就無法用總共的去減去質數部分得到合數部分的和了。
但是,真的不能夠用到Min_25篩了嗎?我們注意到,合數的最大真因子是他本身除以他的最小質因子。最小質因子這個東西好像在哪裡聽過,沒錯就是在講Min_25篩的基本原理的時候,其本質就是用小的質數,一步步把它的倍數給篩掉。每次遞推轉移的時候,把最小質因子為特定質數的數字給篩掉,然後遞推的公式如下:
這裡我們要利用這個公式,當然了F不能是指最大真因子的函式,我們說過了他沒有就積性。事實上,這裡的F是原函式,也即F(x)=x。因為我們的目的其實是要求質數的和,根據合數的最大真因子與其最小質因子有關。更具體的原因的話,後面自然就會理解的。
如果按照我們現在的定義,注意上面等式的右半部分。我們知道原函式是有積性的,所以根據Min_25篩的原理,按順序要把所有最小質因子是某一特定質數的數字篩掉。那麼減去的部分就是這些數字的和,而這個和除以相應的質數,這個結果不就是我們要的最小真因子的和嗎。這樣子相當於在篩的時候,我真正在求的並不是質數的和,而是最小真因子的和,巧妙的利用了原函式的積性。
所以總的來說,這題就是利用Min_25篩的思想,比較間接的求了合數的最大真因子。從這裡我們可以發現,理解一個方法的基本原理還是很重要的。具體見程式碼:
#include<bits/stdc++.h> #define INF 0x3f3f3f3f #define pi 3.141592653589793 #define LL unsigned long long #define mod 998244353 #define pb push_back #define lb lower_bound #define ub upper_bound #define sf(x) scanf("%llu",&x) #define sc(x,y,z) scanf("%lld%lld%lld",&x,&y,&z) using namespace std; const int N = 1000010; LL g[N],s[N],h[N],p[N],w[N],id[N],sz,block; bool isp[N]; inlvoid init(int n) { sz=0; for(int i=2;i<=n;i++) { s[i]=s[i-1]; if(!isp[i])p[++sz]=i,s[i]+=i; for(int j=1;j<=sz&&p[j]*i<n;j++) { isp[i*p[j]]=1; if(i%p[j]==0) break; } } } inline void sieve_g(LL n) { int M=0; for(LL i=1,last;i<=n;i=last+1) { LL len=n/i; last=n/len; w[++M]=len; h[M]=0; if (len&1) g[M]=(len+1)/2*len-1; else g[M]=len/2*(len+1)-1; if(len<=block) id[len]=M; } for(int i=1;i<=sz&&p[i]<=n;i++) for(int j=1;j<=M&&p[i]*p[i]<=w[j];j++) { int op=w[j]/p[i]<=block?id[w[j]/p[i]]:n/(w[j]/p[i]); h[j]+=g[op]-s[p[i]-1]; g[j]-=p[i]*(g[op]-s[p[i]-1]); } } int main() { init(1e6); LL n,m; sf(n); sf(m); n--; LL ans1,ans2; if (n) { block=sqrt(n)+1; sieve_g(n); ans1=h[1]; } else ans1=0; block=sqrt(m)+1; sieve_g(m); ans2=h[1]; printf("%llu\n",ans2-ans1); return 0; }