#pragma GCC optimize("Ofast") #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef long long int ll; typedef unsigned long long ull; mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); ll myRand(ll B) { return (ull)rng() % B; } // kiyoshi先生の実装を拝借… // https://old.yosupo.jp/submission/42911 #include struct countingprime{ public: countingprime(long long N):N(N),N2(sqrtl(N)),N3(cbrtl(N)),N6(sqrtl(N3)),valsize(0),primesize(0){ assert(N>0); solve(); } ~countingprime(){delete[] pi;} //return π(N/k) long long calc(int k=1){ assert(1<=k&&k<=N); return pi[index(N/k)]; } private: long long *val,*pi,*BIT,N,N2,N3,N6; int *prime; size_t valsize,primesize,BITsize; //x≦val[i]を満たす最大のiを返す(x<1,x>Nは壊れる) size_t index(long long x){return x<=N2?valsize-x:N/x-1;} //BITに最小素因数がN^{1/6}以上の合成数now(=N/∛Nであるから列挙に必要な素数は√N以下の物のみで足りていることに注意 void update(unsigned int id,long long now){ if(prime[id]!=now){ //合成数となる時 for(size_t j=valsize-index(now);j=N3)for(j=valsize-j;j>0;j-=-j&j)x-=BIT[j]; pi[i]-=x; } //小さい方valsize-N3個の変更クエリ(BITのaddクエリ) //addする対称はN/N3以下であって最小素因数がN^{1/6}より大きい合成数の個数に等しい //これはO(N^{2/3})個BITで回すのでO(N^{2/3}log N)(実はlogが落ちているらしいが良く分からないし#logは定数 なのでヨシ!) update(see,prime[see]); ++see; } //BITに貯め込んだ分の精算 for(size_t i=1;ival[∛N+1]から =Σ(∛N≦p≦√N,p:prime)Σ(0≦i≦∛N)(int)(p^2<=val[i]) // =Σ(∛N≦p≦√N,p:prime)Σ(1≦i≦∛N+1)(int)(p^2<=floor(N/i)) // =Σ(1≦i≦∛N+1)Σ(∛N≦p≦√N,p:prime)(int)(p^2<=floor(N/i)) // =Σ(1≦i≦∛N+1)Σ(∛N≦p≦√N,p:prime)(int)(p<=floor(√(N/i))) // ≦Σ(1≦i≦∛N+1)π(√(N/i)) // =O((√N/log N)Σ(1≦i≦∛N+1)1/√i) // Σ(1≦i≦N)1/√i~∫_1^N 1/√xdx~2√Nより =O((√N/log N)(N^{1/6})) // =O(N^{2/3}/log N) while(see> L >> R; res = countingprime(R).calc(); if(L > 1) res -= countingprime(L-1).calc(); if(L