[UOJ188]Sanrd(Min_25筛)

题面

https://uoj.ac/problem/188

题解

前置知识

本题要求一个奇怪的函数(f(x))的前缀和:

(f(x)=egin{cases} 0(x{in}prime || x=1) \ x的最大素因子(x为合数,且x中最大素因子的次数{geq}2) \ x的次大素因子(x为合数,且x中最大素因子的次数=1) end{cases})

(x{leq}10^{11})

首先将询问拆分成(calc(r)-calc(l-1))

考虑Min_25筛。计算(calc(n))时,需要对于n的整除集合中的所有数x,预处理出1~x之间的素数个数(g(x))

然后统计答案(s(n,i))时,可以将这里的统计范围缩减为合数,因为素数的(f=0)

假设在(s(n,i))中,已经枚举到了第j个素数的第k次方,那么接下来有两种情况:

  • 这个数在除去(pri[j]^k)之后,只剩下一个素数,那么无论这个素数是不是(pri[j]),最终答案一定都是(pri[j])
  • 这个数在除去(pri[j]^k)之后,还剩下一个合数,那么此时的答案还不能确定,需要递归计算(s(n/pri[j],j+1))

总时间复杂度为(O({frac{n^{frac{3}{4}}}{log n}}))

代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define rg register
#define N 320000

ll l,r,n,sqr,dn;
ll ds[2*N+5]; //整除集合
ll id1[N+5],id2[N+5],g[2*N+5]; //g[i]表示1~i的质数个数 

inline ll id(ll x){ //ds的反函数 
	return x <= sqr ? id1[x] : id2[n/x];
}

ll pn;
ll pri[N+5];
bool isp[N+5];

inline void Eular(){
	pn = 0;
	for(rg ll i = 2;i <= sqr;i++)isp[i] = 1;
	for(rg ll i = 2;i <= sqr;i++){
		if(isp[i])pri[++pn] = i;
		for(rg ll j = 1;i * pri[j] <= sqr;j++){
			isp[i*pri[j]] = 0;
			if(i % pri[j] == 0)break;
		} 
	}
}

inline ll s(ll n,ll i){ //这里只统计合数,因为质数f=0 
	if(pri[i] > n)return 0;
	ll ans = 0;
	for(rg ll j = i;j <= pn && pri[j] * pri[j] <= n;j++){
		for(rg ll cur = pri[j];cur * pri[j] <= n;cur *= pri[j]){
			ans += pri[j] * (g[id(n/cur)] - j + 1); //最后只剩下一个质数,答案为pri[j] 
			ans += s(n / cur,j + 1); //最后剩下一个合数 
		}
	} 
	return ans;
}

inline ll calc(ll x){
	n = x;
	sqr = (ll)floor(sqrt(n) + 1e-6);
	Eular();
	dn = 0;
	ll L,R = 0;
	while(R < n){
		L = R + 1,R = n / (n / L);
		ds[++dn] = n / L;
		if(n / L <= sqr)id1[n/L] = dn;
		else id2[R] = dn;
		g[dn] = ds[dn] - 1;
	}
	for(rg ll j = 1;j <= pn;j++)
		for(rg ll i = 1;i <= dn && ds[i] >= pri[j] * pri[j];i++)
			g[i] -= (g[id(ds[i]/pri[j])] - (j-1));
	return s(n,1);
}

int main(){
	cin >> l >> r;	
	cout << calc(r) - calc(l-1) << endl;
	return 0;
}

原文地址:https://www.cnblogs.com/xh092113/p/12300195.html