[杜教筛] Codechef January Challenge 2018 #SQRGOOD Simplify the Square Root

二分转化为 μ2 的前缀和。
然后转化为 O(n13) 的运算,但是需要预处理 μ 的前缀和,大力杜教筛求和。
然后感谢阿爷教我把二分改成了迭代,小范围内一个一个挪,用rho求 μ(n) ,然后就能卡进去了。
复杂度似乎是萎的吧。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<map>
#include<algorithm>
#define pb push_back
using namespace std;
typedef long long ll;

namespace Rho{
  int prime[9]={2,3,5,7,11,13,17,19,23};  
  unsigned long long RR;  
  inline long long R(long long Mo){return (RR+=4179340454199820289ll)%Mo;}  
  inline long long mul(long long x,long long y,long long Mo){  
    long long tmp=(x*y-(long long)((long double)x/Mo*y+0.5)*Mo);  
    return tmp<0?tmp+Mo:tmp;  
  }  
  inline long long Pow(long long x,long long k,long long Mo){  
    long long ans=1;  
    for(;k;k>>=1,x=mul(x,x,Mo))if(k&1)ans=mul(ans,x,Mo);  
    return ans;  
  }  
  inline bool MR(long long n){  
    if(n<=1)return false;  
    for(int i=0;i<9;i++)if(n==prime[i])return true;  
    long long d=n-1;int tmp=0;  
    while((d&1)==0)d>>=1,tmp++;  
    for(int i=0;i<9;i++){  
      long long x=Pow(prime[i],d,n),p=x;  
      for(int j=1;j<=tmp;j++){  
    x=mul(x,x,n);  
    if((x==1)&&(p!=1)&&(p!=n-1))return false;  
    p=x;  
      }  
      if(x!=1)return false;  
    }  
    return true;  
  }  
  inline long long f(long long x,long long c,long long Mo){return (mul(x,x,Mo)+c)%Mo;}  
  inline long long gcd(long long x,long long y){return x==0?y:gcd(y%x,x);}  
  inline long long check(long long c,long long n){  
    long long x=R(n),y=f(x,c,n),p=n;  
    while((x!=y)&&((p==n)||(p==1))){  
      if(x>y)p=gcd(n,x-y);  
      else p=gcd(n,y-x);  
      x=f(x,c,n);y=f(f(y,c,n),c,n);  
    }  
    return p;  
  }

  vector<ll> v;

  inline void rho(long long n){  
    if(n<=1)return;  
    if(MR(n)){v.pb(n);return;}  
    while(true){  
      long long tmp=check(R(n-1)+1,n);  
      if(tmp!=n && tmp!=1){rho(tmp),rho(n/tmp);return;}  
    }  
  }

  inline bool nmu2(ll n){
    v.clear(); rho(n);
    sort(v.begin(),v.end());
    for (int i=0;i<v.size();i++) if (v[i]==v[i+1]) return 1;
    return 0;
  }
}

const int maxn=20000000;
const int N=maxn+5;

int prime[7000000],num;
int mu[N],mu2[N]; 

const int P=10000007;

inline void Pre(int n){
  mu[1]=1; int *vst=mu2;
  for (int i=2;i<=n;i++){
    if (!vst[i]) prime[++num]=i,mu[i]=-1; ll t;
    for (int j=1;j<=num && (t=(ll)i*prime[j])<=n;j++){
      vst[t]=1;
      if (i%prime[j]==0){
    mu[t]=0; break;
      }else
    mu[t]=-mu[i];
    }
  }
  for (int i=1;i<=n;i++) mu2[i]=((bool)mu[i])+mu2[i-1],mu[i]=mu[i-1]+mu[i];
}

map<int,int> Map;  

inline int Sum(int n){  
  if (n<=maxn) return mu[n];  
  if (Map.find(n)!=Map.end()) return Map[n];  
  int tem=1; int l,r;  
  for (l=2;l*l<=n;l++) tem-=Sum(n/l);  
  for (int t=n/l;l<=n;l=r+1,t--){  
    r=n/t;  
    tem-=(r-l+1)*Sum(t);  
  }  
  return Map[n]=tem;  
}

inline ll S(ll n){
  if (n<=maxn) return n-mu2[n];
  int x=sqrt(n); ll ret=0,cur,lst=0;
  for (int i=1,j;i<=x;i=j+1){
    ll t=n/i/i; j=sqrt(n/t);
    ret+=((cur=Sum(j))-lst)*t;
    lst=cur;
  }
  return n-ret;
}

const double PI=acos(-1.0);
const double _K=1.0/(1-6.0/PI/PI);
const double K=2.550546096730430440286486962;

ll n;

inline ll Mod(ll x,ll f){
  if (f>=n){
    while (f>=n){
      f-=Rho::nmu2(x);
      if (f<n) return x;
      x--;
    }
  }else{
    while (f<n){
      f+=Rho::nmu2(x+1);
      if (f>=n) return x+1; 
      x++;
    }
  }
}

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  Pre(maxn);
  scanf("%lld",&(n));
  ll L,R,MID,f1,f2;
  L=1; R=max(1e10,2.550546098*n);
  f1=S(L); f2=S(R);
  while (L+1<R){
    if (f2-n<=1000){
      R=Mod(R,f2);
      break;
    }
    if (n-f1<=1000){
      R=Mod(L,f1);
      break;
    }
    MID=L+(long double)(n-f1)/(f2-f1)*(R-L);
    ll f=S(MID);
    if (f==n){
      R=Mod(MID,f); break;
    }
    if (f>n)
      f2=f,R=MID;
    else
      f1=f,L=MID;
  }
  printf("%lld\n",R);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值