[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;
}

猜你喜欢

转载自www.cnblogs.com/xh092113/p/12300195.html
今日推荐