UOJ188 Sanrd Min_25筛

时间:2022-06-25 01:20:37

传送门


省选之前做数论题会不会有Debuff啊

这道题显然是要求\(1\)到\(x\)中所有数第二大质因子的大小之和,如果不存在第二大质因子就是\(0\)

线性筛似乎可以做,但是\(10^{11}\)的数据范围让人望而却步,而杜教筛需要对\(f(x)\)找到一个函数\(g(x)\)做狄利克雷卷积成为一个好算前缀和的函数\(h(x)\),相信各位是找不到这样一个函数的。所以考虑Min_25筛。但用Min_25筛还不知道要筛什么东西,故从Min_25筛最后的计算过程入手。

Min_25筛的每一层递归中计算了两种数对答案的贡献:①当前被拼出的数乘上一个质数的若干次方产生的数的贡献;②当前被拼出的数乘上一个质数的若干次方再乘上其他质数产生的数的贡献。对于②的贡献我们递归处理,所以只需考虑①的情况。

①中,如果乘上的质数的指数\(> 1\),产生贡献的就是当前质因子,否则就是上一个被乘上的因子。上一个被乘上的因子在递归过程中已经传递了,所以我们只需要上一个被乘上的因子产生了多少次贡献,即在一段区间内共有多少个质数。所以筛出\(\forall x \in [1,N] \sum\limits_{i=1}^{\frac{N}{x}} [i \in Prime]\)就可以计算答案。

#include<bits/stdc++.h>
using namespace std; #define int long long
inline int read(){
int a = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return a;
} const int MAXN = 1e6 + 7;
int prm[MAXN] , cnt;
bool nprm[MAXN]; void init_prm(){
for(int i = 2 ; i <= 1e6 ; ++i){
if(!nprm[i])
prm[++cnt] = i;
for(int j = 1 ; i * prm[j] <= 1e6 ; ++j){
nprm[i * prm[j]] = 1;
if(i % prm[j] == 0) break;
}
}
} int id1[MAXN] , id2[MAXN] , val[MAXN << 1] , f[MAXN << 1] , N , T , Cnt; int find(int x){return x <= T ? id1[x] : id2[N / x];} void init_Min25(){
T = sqrt(N); Cnt = 0;
for(int i = 1 , pi ; i <= N ; i = pi + 1){
int cur = N / i;
pi = N / cur;
val[cur <= T ? id1[cur] = ++Cnt : id2[pi] = ++Cnt] = cur;
f[Cnt] = cur - 1;
}
for(int i = 1 ; i <= cnt && prm[i] * prm[i] <= N ; ++i){
int p = find(prm[i - 1]);
for(int j = 1 ; val[j] >= prm[i] * prm[i] ; ++j){
int q = find(val[j] / prm[i]);
f[j] -= f[q] - f[p];
}
}
} int solve(int x , int p){
if(prm[p] > x) return 0;
int sum = (f[find(x)] - f[find(prm[p - 1])]) * prm[p - 1];
for(int j = p ; j <= cnt && prm[j] * prm[j] <= x ; ++j){
int times = prm[j];
while(times * prm[j] <= x){
sum += solve(x / times , j + 1) + prm[j];
times *= prm[j];
}
}
return sum;
} int work(){init_Min25(); return solve(N , 1);} signed main(){
init_prm();
cin >> N; --N; int sum = work();
cin >> N; cout << work() - sum;
return 0;
}