[ 杜教筛 数论 ] Codechef January Challenge 2018 #SQRGOOD

题目大意

给你一个数 n(n1018) ,求第 n 个含大于 1 平方因子的数。

比赛时卡常卡到头秃
发现一个数 x 含有平方因子等价于 μ2x=0
si 表示 μ2 的前缀和,那么答案要满足的条件就是 anssans=n
二分答案,然后问题就是怎么求 si
有一个公式:

μ2n=d2|nμd

那么
sn=i=1nμ2n=i=1nd2|nμd=d=1nμdnd2

这样 μ 的前缀和可以用杜教筛求, sn 用分块求出。
可以证明复杂度为 O(n13log2n)
但这样还不能通过此题。
有一个结论:答案的一个接近值是 n16π2 ( 证明戳这里)。
所以直接在接近值周围一个范围内二分即可。
时间复杂度 O(n13lognloglogn)

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=60000010;
const double pi=acos(-1.0);
typedef long long ll;
map<int,int>M;
ll n,l,r,Mid,Ans;
int mu[N],mu2[N],p[N>>3],num;
bool b[N];
inline void Init(){
    mu[1]=mu2[1]=1;
    for(int i=2;i<N;i++){
        if(!b[i]){
            p[++num]=i;mu[i]=-1;
        }
        int t;
        for(int j=1;j<=num&&(t=p[j]*i)<N;j++){
            b[t]=1;
            if(!(i%p[j])){
                mu[t]=0;
                break;
            }
            mu[t]=-mu[i];
        }
    }
    for(int i=2;i<N;i++)mu2[i]=mu2[i-1]+(!mu[i]?0:1),mu[i]+=mu[i-1];
}
int Solve(int n){
    if(n<N)return mu[n];
    if(M.count(n))return M[n];
    int Ans=1;
    for(int i=2;i<=n;){
        int k=n/i,j=n/k;
        Ans-=Solve(k)*(j-i+1);
        i=j+1;
    }
    return M[n]=Ans;
}
inline ll Get(ll n){
    if(n<N)return mu2[n];
    ll Ans=0,lst=0,tmp,t,i=1;
    for(;i*i*i<=n;i++)Ans+=(n/(i*i))*((tmp=mu[i])-lst),lst=tmp;
    for(Ans-=(t=n/(i*i))*lst;t;t--)Ans+=Solve(sqrt(n/t));
    return Ans;
}
int main(){
    scanf("%lld",&n);
    Init();
    l=n/(1-6/pi/pi),r=l+400000,l-=400000;
    l=max(l,1ll);
    while(l<=r){
        Mid=l+r>>1;
        if(Mid-Get(Mid)>=n)Ans=Mid,r=Mid-1;else l=Mid+1;
    }
    cout<<Ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值