UOJ#188. 【UR #13】Sanrd(min25篩)
UOJ#188. 【UR #13】Sanrd(min25篩)
題目連結:https://uoj.ac/problem/188
題目大意:定義數論函式
f
(
x
)
f(x)
f(x) 的值為
x
x
x 的第二大質因子,其中質因子可以重複貢獻,例如
f
(
36
)
=
3
f(36)=3
f(36)=3 ,且當且僅當
x
∈
p
r
i
m
e
o
r
x
=
1
,
f
(
x
)
=
0
x\in prime\space or\space x=1,f(x)=0
x∈primeorx=1,f(x)=0 。給定區間
[
l
,
r
]
[l,r]
[l,r] ,求
∑
i
=
l
r
f
(
i
)
\sum\limits_{i=l}^rf(i)
題解:對於這樣的一個數論函式,顯然我們幾乎是無法構造合適的數論函式
g
(
x
)
g(x)
g(x) ,使得能夠利用杜教篩去求
f
(
x
)
f(x)
f(x) 的字首和。那我們要往min25篩的解決方向去想,但這個函式並不是積性函式,要如何魔改我們的min25篩,從而可以求出這樣一個怪異的函式的字首和呢?回想一下min25篩中
S
(
i
,
j
)
S(i,j)
S(i,j) 的轉移式,我們有
S
(
i
,
j
)
=
g
[
i
]
−
s
u
m
[
j
]
+
∑
k
=
j
+
1
n
u
m
∑
r
=
1
p
k
r
≤
i
f
(
p
k
r
)
(
S
(
⌊
i
p
k
r
⌋
,
k
)
+
[
r
>
1
]
)
S(i,j)=g[i]-sum[j]+\sum\limits_{k=j+1}^{num}\sum\limits_{r=1}^{p_{k}^{r}\leq i}f(p_k^r)(S(\lfloor\frac{i}{p_{k}^{r}}\rfloor,k)+[r>1])
下面是AC程式碼。
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cerr<<#x<<":"<<x<<endl
#define debug2(x,y) cerr<<#x<<":"<<x<<" "<<#y<<":"<<y<<endl
#define debug3(x,y,z) cerr<<#x<<":"<<x<<" "<<#y<<":"<<y<<" "<<#z<<":"<<z<<endl
#define int unsigned long long
struct fastio{
fastio(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
}fio;
int N,sqN,num,cnt;
const int MAX=2e6+5;
int prime[MAX],part[MAX],sum[MAX],g[MAX],ind1[MAX],ind2[MAX],w[MAX];
inline int getind(int x){
if(x>sqN) return ind2[N/x];
return ind1[x];
}
void init(){
sqN=sqrt(N);
num=cnt=0;
memset(prime,0,MAX);
memset(part,0,MAX);
memset(sum,0,MAX);
memset(g,0,MAX);
memset(ind1,0,MAX);
memset(ind2,0,MAX);
memset(w,0,MAX);
for(int i=2;i<=sqN;++i){
if(!part[i]){
prime[++num]=i;
part[i]=i;
sum[num]=sum[num-1]+1;
}
for(auto j:prime){
if(!j) continue;
if(i*j>sqN) break;
part[i*j]=j;
if(i%j==0) break;
}
}
for(int l=1,r;l<=N;l=r+1){//notice! g[cnt] must decrease 1!(because f(1) is out of range)
w[++cnt]=N/l;
r=N/w[cnt];
if(w[cnt]>sqN) ind2[r]=cnt;
else ind1[w[cnt]]=cnt;
g[cnt]=w[cnt]-1;
}
for(int j=1;j<=num;++j){
for(int i=1;i<=cnt&&prime[j]*prime[j]<=w[i];++i){
int ind=getind(w[i]/prime[j]);
g[i]-=g[ind]-sum[j-1];
}
}
}
int S(int i,int j){
if(prime[j]>=i) return 0;
int res=prime[j]*(g[getind(i)]-sum[j]);
for(int k=j+1;k<=num&&prime[k]*prime[k]<=i;++k){
for(int r=1,t=prime[k];t<=i;t*=prime[k],++r){//notice! t cannot be moduled!
res+=S(i/t,k)+prime[k]*(r>1);
}
}
return res;
}
signed main(){
int L,R,ans;
cin>>L>>R;
N=L-1;
init();
ans=S(N,0);
N=R;
init();
cout<<S(N,0)-ans<<endl;
}
第一次寫min25篩題,用了約3個小時吧(蒟蒻線上爬爬爬)。估計因為兩次開了預處理的原理,時間有點慢了。