結果
| 問題 |
No.1661 Sum is Prime (Hard Version)
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2021-08-27 22:47:02 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 512 ms / 3,000 ms |
| コード長 | 6,002 bytes |
| コンパイル時間 | 3,087 ms |
| コンパイル使用メモリ | 215,896 KB |
| 最終ジャッジ日時 | 2025-01-24 03:27:08 |
|
ジャッジサーバーID (参考情報) |
judge2 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 22 |
ソースコード
#pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <array>
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<bits/stdc++.h>
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^{1/6}*√N=N^{2/3}>=N/∛Nであるから列挙に必要な素数は√N以下の物のみで足りていることに注意
void update(unsigned int id,long long now){
if(prime[id]!=now){
//合成数となる時
for(size_t j=valsize-index(now);j<BITsize;j+=-j&j)++BIT[j];
}
//nowの倍数を追加する
while(id<primesize&&now*prime[id]<N/N3){
update(id,now*prime[id]);
++id;
}
}
void solve(){
{
//val,pi,prime(√N以下)の初期化
//val={floor(N/i)|1≦i≦N}を重複を取り除き降順に並び替えたもの
//pi:π(val[i])にしたい、初期化はpi[i]=val[i]で
for(long long now=N;now;now=N/(N/now+1))++valsize;
val=new long long[valsize];
pi=new long long[valsize];
valsize=0;
for(long long now=N;now;now=N/(N/now+1)){
val[valsize]=pi[valsize]=now;
valsize++;
}
bool *isprime=new bool[N2+1];
memset(isprime+2,1,N2-1);
for(int i=2;i*i<=N2;++i){
if(isprime[i])for(int j=i*i;j<=N2;j+=i)isprime[j]=false;
}
for(int i=2;i<=N2;++i)if(isprime[i])++primesize;
prime=new int[primesize];
primesize=0;
for(int i=2;i<=N2;++i)if(isprime[i])prime[primesize++]=i;
delete[] isprime;
}
//単純にエラトステネスの篩と同じように小さい素数から篩っていく
//dp[j]-=dp[j/prime[i]]みたいなことをすればいい(値が飛び飛びなので適宜調整する(この調整自体はO(1)でできる))
//完全愚直でやると√N*π(√N)=O(N/log N),700msくらい(π(N)=O(N/log N)に注意)
//BITを上手く使うとO(N^{2/3})となる
size_t see=0;
{
//愚直に更新する(p≦N^{1/6})
//π(N^{1/6})*2√N=O(N^{2/3}/log N)
while(see<primesize&&prime[see]<=N6){
for(size_t i=0;prime[see]<=val[i]/prime[see];++i){
pi[i]-=pi[index(val[i]/prime[see])]-see-1;
}
++see;
}
}
{
//BITを使う(N^{1/6}<p<∛N)
BITsize=valsize-N3+1;
BIT=new long long[BITsize];
memset(BIT,0,sizeof(long long)*BITsize);
while(see<primesize&&prime[see]<N3){
//大きい方N3個の変更クエリ(BITのsumクエリ)
//∛N*(π(∛N)-π(N^{1/6})=O(N^{2/3}/log N)回BITでクエリを回すので
//O(N^{2/3})
for(size_t i=0;(int)i<N3&&prime[see]<=val[i]/prime[see];++i){
size_t j=index(val[i]/prime[see]);
long long x=pi[j]-see-1;
if((int)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;i<BITsize;++i){
BIT[i]+=BIT[i-(-i&i)];
pi[valsize-i]-=BIT[i];
}
delete[] BIT;
}
{
//愚直に更新する(∛N≦p≦√N)
//実はO(N^{2/3}/log N)
//(∵)Σ(∛N≦p≦√N,p:prime)#{i|p≦val[i]/p}=Σ(∛N≦p≦√N,p:prime)#{i|p^2≦val[i]}
// ∛N≦pより,p^2>val[∛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<primesize){
for(size_t i=0;prime[see]<=val[i]/prime[see];++i){
pi[i]-=pi[index(val[i]/prime[see])]-see-1;
}
++see;
}
}
//1を取り除く
for(size_t i=0;i<valsize;++i)--pi[i];
delete[] val;
}
};
int main(){
cin.tie(nullptr);
ios::sync_with_stdio(false);
ll res = 0;
ll L,R; cin >> L >> R;
res = countingprime(R).calc();
if(L > 1) res -= countingprime(L-1).calc();
if(L<R){
res += countingprime(R+R-1).calc();
res -= countingprime(L+L).calc();
}
cout << res << endl;
}