LOJ6625 时间复杂度(min_25筛)

本人在LOJ的第三题(前两题太水不好意思说了QwQ),欢迎大家踩std。

题目链接:LOJ

题目大意:定义函数 $f$:($minp$ 表示最小质因子)

$$f(x)=\begin{cases}0&x=1\\x-2&x\in prime\\minp(x)-1&otherwise\end{cases}$$

求 $f$ 前 $n$ 项的和对 $20190601$ 取模的值。

$1\le n\le 10^{11}$。


考虑 min_25 筛中的 $g_0(n,j)$($0$ 次方,即满足条件的数的个数)和 $g_0(n,j-1)$。

$g_0(n,j)=\sum\limits^n_{i=1}[i\in prime\ ||\ minp(i)>p_j]$

$g_0(n,j-1)=\sum\limits^n_{i=1}[i\in prime\ ||\ minp(i)\ge p_j]$

特别地,有 $g_0(n,0)=n-1$。

所以 $g_0(n,j-1)-g_0(n,j)=\sum\limits^n_{i=1}[i\notin prime\ \&\&\ minp(i)=p_j]$。

也就是最小质因子为 $p_j$ 的数的个数。(不包括 $p_j$)

那么答案为 $\sum\limits^{|P|}_{j=1}p_j(g_0(n,j-1)-g_0(n,j)+1)-(n-1)-g_0(n,|P|)$。

……吗?

由于 min_25 筛只能处理到 $\le \sqrt{n}$ 的质数,所以 $>\sqrt{n}$ 的质数不会被统计到。

所以分质数和合数两类讨论。合数部分上面讨论完了。质数部分是模板。

答案为 $\sum\limits^{|P|}_{j=1}p_j(g_0(n,j-1)-g_0(n,j))+g_1(n,|P|)-(n-1)-g_0(n,|P|)$。

时间复杂度 $O(\dfrac{n^{3/4}}{\log n})$。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=700070,mod=20190601;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
ll n,w[maxn];
int sq,tot,g1[maxn],g0[maxn],s1[maxn],pr[maxn],pl,id1[maxn],id2[maxn],g[maxn],ans,mn[maxn];
bool vis[maxn];
inline int add(int x,int y){return x+y<mod?x+y:x+y-mod;}
inline int sub(int x,int y){return x<y?x-y+mod:x-y;}
inline int id(ll x){return x<=sq?id1[x]:id2[n/x];}
void init(){
    sq=sqrt(n);
    FOR(i,2,sq){
        if(!vis[i]) pr[++pl]=i,s1[pl]=(s1[pl-1]+i)%mod;
        FOR(j,1,pl){
            if(i*pr[j]>sq) break;
            vis[i*pr[j]]=true;
            mn[i*pr[j]]=pr[j];
            if(i%pr[j]==0) break;
        }
    }
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        w[++tot]=n/l;
        if(n/l<=sq) id1[n/l]=tot;
        else id2[n/(n/l)]=tot;
        int t=w[tot]%mod;
        g0[tot]=sub(t,1);
        g1[tot]=(1ll*t*(t+1)/2-1)%mod;
    }
}
void calc_g(){
    FOR(i,1,pl) FOR(j,1,tot){
        if(1ll*pr[i]*pr[i]>w[j]) break;
        int pre=g0[j];
        g0[j]=sub(g0[j],sub(g0[id(w[j]/pr[i])],i-1));
        g1[j]=sub(g1[j],1ll*pr[i]*sub(g1[id(w[j]/pr[i])],s1[i-1])%mod);
        if(w[j]==n) g[i]=sub(pre,g0[j]);
    }
}
int main(){
    scanf("%lld",&n);
    init();
    calc_g();
    FOR(i,1,pl) ans=add(ans,1ll*pr[i]*g[i]%mod);
    ans=add(ans,sub(g1[id(n)],g0[id(n)]));
    ans=sub(ans,(n-1)%mod);
    printf("%d\n",ans);
}
View Code

 

转载于:https://www.cnblogs.com/1000Suns/p/10888832.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值