zoukankan      html  css  js  c++  java
  • [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;
    }
    
    
  • 相关阅读:
    出错处理函数abort、exit、atexit、strerror. . .
    linux查询系统信息命令
    [转载]比google和百度强十倍的搜索类网站
    陶  朱  商  经
    ip的划分,超详细.【网管常识】
    linux的hostname修改详解
    勤于寻找谈话资料
    Windows常用命令集
    C语言中printf格式
    How to disable SELinux
  • 原文地址:https://www.cnblogs.com/xh092113/p/12300195.html
Copyright © 2011-2022 走看看