[UOJ188]Sanrd(Min_25筛)

题面

https://uoj.ac/problem/188

题解

前置知识

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

\(f(x)=\begin{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;
}

posted @ 2020-02-12 18:23  coder66  阅读(191)  评论(0编辑  收藏  举报