[模板]min_25筛

题目

传送门

题解

对于原函数 \(f(p^k)=p^k(p^k-1)\),我们可以将其写作 \(f(x)=x^2-x,x\in \Bbb P\),然后,分解成俩完全积性函数:

\[f_1=x \\ f_2=x^2 \]

考虑 \(\tt min\_25\) 筛,有

\[g(i,j)= \begin{cases} \sum_{k=2}^i f(k)\quad ,j=0 \\ g(i,j-1)\quad ,i<\text{prime}_j^2 \\ g(i,j-1)-f(\text{prime}_j)\left(g\left(\left\lfloor{i\over \text{prime}_j}\right\rfloor,j-1\right)-\sum_{k=1}^{k<j}f(\text{prime}_k)\right)\quad ,i\ge \text{prime}_j^2 \end{cases} \]

那么,对于 \(g_0\)

\[g_0(i,j)= \begin{cases} \sum_{k=2}^i k\quad ,j=0 \\ g(i,j-1)\quad ,i<\text{prime}_j^2 \\ g(i,j-1)-\text{prime}_j\left(g\left(\left\lfloor{i\over \text{prime}_j}\right\rfloor,j-1\right)-\sum_{k=1}^{k<j}\text{prime}_k\right)\quad ,i\ge \text{prime}_j^2 \end{cases} \]

要处理这个,需要处理出质数的前缀和.

同时,对于 \(g_1\)

\[g_1(i,j)= \begin{cases} \sum_{k=2}^i k^2\quad ,j=0 \\ g(i,j-1)\quad ,i<\text{prime}_j^2 \\ g(i,j-1)-\text{prime}_j^2\left(g\left(\left\lfloor{i\over \text{prime}_j}\right\rfloor,j-1\right)-\sum_{k=1}^{k<j}\text{prime}_k^2\right)\quad ,i\ge \text{prime}_j^2 \end{cases} \]

要处理这个,需要处理出质数的平方的前缀和.

由于我们的 \(n\le 10^{10}\),如果直接用其作为下标会炸掉,我们考虑对于所有的 \(\left\lfloor \frac{n}{i}\right\rfloor\) 进行离散:

/** @brief 对于 2\sqrt n 个不同的数值进行离散*/
ll w[maxn+5];int wcnt;
/** @brief 存放两种情况的下标*/
int id[2][maxn+5];
/** @brief
 *  得到每种 n/i 的值所归属的块的编号
 *  数值大的编号小, 数值小的编号大
*/
inline void hash_id(){
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        w[++wcnt]=n/l;
        if(w[wcnt]<=sqn)id[0][w[wcnt]]=wcnt;
        else id[1][n/w[wcnt]]=wcnt;
    }
}

如果我们现在有一个 \(x\),如果我们要得到他的 \(id\),那么我们还需要写一个函数:

/** @brief @p x 归属于哪个编号*/
inline int getid(const ll x){
    if(x<=sqn)return id[0][x];
    return id[1][n/x];
}

要求 \(S\) 直接照着打就可以了.

代码

const int mod=1e9+7;
const int maxn=2e6;
const int inv6=166666668;

int sie[maxn+5];
int prime[maxn>>2],pcnt;
/** @brief g 有两项,这里分开求*/
int g1[maxn+5],g2[maxn+5];
/** @brief 质数前缀和*/
int ppre[maxn+5];
/** @brief 质数平方的前缀和*/
int pow_ppre[maxn+5];

inline void sieve(const int up){
    sie[1]=1;
    rep(i,2,up){
        if(!sie[i])prime[++pcnt]=i;
        for(int j=1;j<=pcnt && i*prime[j]<=up;++j){
            sie[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
    rep(i,1,pcnt){
        ppre[i]=(ppre[i-1]+prime[i])%mod;
        pow_ppre[i]=(pow_ppre[i-1]+1ll*prime[i]*prime[i]%mod)%mod;
    }
}

ll n;int sqn;
/** @brief 对于 2\sqrt n 个不同的数值进行离散*/
ll w[maxn+5];int wcnt;
/** @brief 存放两种情况的下标*/
int id[2][maxn+5];

/** @brief @p x 归属于哪个编号*/
inline int getid(const ll x){
    if(x<=sqn)return id[0][x];
    return id[1][n/x];
}

/** @brief
 *  得到每种 n/i 的值所归属的块的编号
 *  数值大的编号小, 数值小的编号大
*/
inline void hash_id(){
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        /** @brief 对于所有 i∈[l,r], 这一块的 n/i 都是 w[wcnt]*/
        w[++wcnt]=n/l;
        /** @brief w[wcnt] 的编号是多少*/
        if(w[wcnt]<=sqn)id[0][w[wcnt]]=wcnt;
        else id[1][n/w[wcnt]]=wcnt;
    }
}

inline int sum(ll x){x%=mod;
    return (1ll*x*(x+1)/2)%mod;
}

inline int squa(ll x){x%=mod;
    return 1ll*x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}

inline void getg(){
    /** @brief g 的初始化, -1 是为了去掉 f(1)*/
    rep(i,1,wcnt){
        g1[i]=sum(w[i])-1;
        g2[i]=squa(w[i])-1;
    }
    rep(j,1,pcnt){
        for(int i=1;i<=wcnt && 1ll*prime[j]*prime[j]<=w[i];++i){
            /** @brief 此处应考虑 id 的特性——数值大的编号小, 所以才可以这样写*/
            g1[i]=(g1[i]+mod-1ll*prime[j]*(g1[getid(w[i]/prime[j])]+mod-ppre[j-1])%mod)%mod;
            g2[i]=(g2[i]+mod-1ll*prime[j]*prime[j]%mod*(g2[getid(w[i]/prime[j])]%mod-pow_ppre[j-1])%mod)%mod;
        }
    }
}

int S(const ll x,const int j){
    if(prime[j]>x)return 0;
    int ret=(g2[getid(x)]+mod-g1[getid(x)])%mod;
    ret=(ret+mod-(g2[getid(prime[j])]+mod-g1[getid(prime[j])])%mod)%mod;
    // 等价形式 : ret=(ret+mod-(pow_ppre[j]+mod-ppre[j])%mod)%mod;
    for(int i=j+1;i<=pcnt && 1ll*prime[i]*prime[i]<=x;++i)
        for(ll e=1,sp=prime[i];sp<=x;sp*=prime[i],++e)
            ret=(ret+1ll*sp%mod*(sp%mod-1)%mod*(S(x/sp,i)+(e>1))%mod)%mod;
    return ret;
}

signed main(){
    n=readin(1ll);sqn=sqrt(n);
    sieve(sqn);
    hash_id();
    getg();
    writc((S(n,0)+1)%mod,'\n');
    return 0;
}

用到の小 \(\tt trick\)

十分单纯的 \(\tt min\_25\) 筛板子题.

posted @ 2021-02-01 19:31  Arextre  阅读(88)  评论(0编辑  收藏  举报