- 靜態數組內存是受不了的。但同學好機智,用
vector 強行開150000000 的數組,水了過去。。。 - 令
f(x) 表示範圍[1,x] 內滿足題意的t 的個數,那麼我們考慮打表求解,顯然,不可以直接打表,所以我們考慮將ans=f(r)−f(l−1) 3 億分段,每1000000 分一段,於是總共需要預處理出300 個數,即這在本地是可以f(0),f(1000000),f(2000000),…,f(300000000) O(n) 地得到的(Eular 篩法O(n) +枚舉完全平方數O(n) ),然後對一個詢問f(x) ,只需要在線求解x 這一部分即可。 - 我們舉例說明求解
f(11111111) 的方法,查上述規模爲300 的表格,我們發現有f(11000000) 可用,也就是已知[1,11000000] 內滿足題意的數的個數;接下來,只需要求解範圍[11000001,11111111] 內滿足題意的個數即可,由於我們每1000000 做一個分割,因此我們真正需要在線做的這部分規模不會大於1000000 ,就可以使用你喜歡的篩素數方法通過了。爲了簡潔,後面這部分窩就直接O(nn−−√) 地做了。 - 本地離線預處理部分:
/* **********************************************
File Name: c_gen.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年09月07日 星期一 21時35分46秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int MAX = 300000001;
const int MAX_SQ = 20001;
bool is_prime[MAX];
bool yes[MAX];
int sq[MAX_SQ], sz = 0;
int prime[MAX >> 4], pr = 0;
int init() {
memset(is_prime, true, sizeof(is_prime));
is_prime[0] = is_prime[1] = false;
for (int i = 2; i < MAX; ++i) {
if (is_prime[i]) {
prime[pr++] = i;
}
for (int j = 0; j < pr && prime[j] <= MAX / i; ++j) {
is_prime[prime[j] * i] = false;
if (i % prime[j] == 0) break;
}
}
for (int i = 1; i * i < MAX; ++i) {
sq[sz++] = i * i;
}
return pr;
}
int main() {
//printf("%d\n", init());
init();
memset(yes, false, sizeof(yes));
for (int i = 0; i < sz; ++i) {
for (int j = i; j < sz; ++j) {
int x = sq[i] + sq[j];
if (x >= MAX) break;
if (is_prime[x]) {
yes[x] = true;
}
}
}
int sum = 0;
const int CUT = 1000000;
for (int i = 1; i < MAX; ++i) {
if (yes[i]) ++sum;
if (i % CUT == 0) {
printf("%d, ", sum);
sum = 0;
}
}
return 0;
}
Linux 下可在終端用下述命令將打表結果添加到c.cpp
後面,避免手動粘貼疏漏g++ c_gen.cpp -o c_gen –std=c++11
./c_gen >> c.cppWindows 也類似,注意確認是否已手動將g++.exe
添加進環境變量,否則cmd
下直接運行g++
是無法找到該程序的。具體請自行百度之。。。g++ c_gen.cpp -o c_gen -std=c++11
c_gen >> c.cpp注意二者的區別是運行生成的
c_gen
的方式不同,Linux 直接輸入命令是隻在$PATH
環境變量中包含的路徑查找,並且不包含當前目錄。試想,由於Linux 下任何用戶都有/tmp
的rwx
權限,一個普通用戶在裏面放置了一個惡意程序,甚至命名爲ls
(ls
原本爲系統程序,位於/bin/ls
,功能爲列出當前目錄下的文件/目錄)!之後root 在tmp
中想查看文件,然後執行ls
。。。後果可想而知。。。也正因此,Linux 執行當前目錄下的程序需要輸入./程序名
,相當於要強調“俺要運行當前目錄下那個啥啥啥。。。”扯遠了。。。總之上述完成後,我們就得到想要的表了,當然,窩打的表是分段的,拿來用之前需要先處理出它的前綴和,即打的表統計的是
[1,1000000],[1000001,2000000],… 各段的數量,我們真正需要的是比如[1,3000000] 這樣的形式,於是相當於需要前3 項的和,又如[1,11000000] 需要的是前11 項的和,隨便一提,前綴和就是前若干項和。。。- 然後就是下面的處理
/* **********************************************
File Name: c.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年09月07日 星期一 21時35分52秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 300000007;
const int EVE = 1000000;
const int MAX_SQ = 20001;
const int MAX = 301;
/*
* tab[1]: 1 ~ EVE
* tab[2]: EVE + 1 ~ 2EVE
* ..
* tab[MAX-1]: (MAX-2)*EVE + 1 ~ (MAX-1)*EVE
*/
int tab[MAX] = {0, 39176, 35241, 33867, 33219, 32691, 32139, 31928, 31499, 31341, 31080, 30899, 30913, 30576, 30405, 30301, 30139, 30087, 30002, 29854, 29814, 29600, 29719, 29406, 29422, 29436, 29274, 29336, 29150, 29172, 29094, 29091, 28875, 28873, 28902, 28653, 28729, 28749, 28687, 28754, 28595, 28584, 28417, 28392, 28489, 28355, 28315, 28386, 28313, 28256, 28258, 28169, 28080, 28070, 28052, 28033, 28035, 27971, 28017, 27892, 27899, 27976, 27713, 27847, 27868, 27831, 27821, 27783, 27794, 27716, 27699, 27611, 27660, 27723, 27534, 27588, 27680, 27510, 27458, 27448, 27443, 27498, 27578, 27454, 27448, 27327, 27347, 27304, 27472, 27326, 27266, 27219, 27227, 27345, 27189, 27308, 27228, 27199, 27167, 27086, 27143, 27101, 27097, 27178, 27021, 27107, 27013, 26975, 27086, 27143, 27133, 26917, 27074, 26976, 26792, 26905, 26928, 26827, 26892, 26881, 26925, 26796, 26823, 26879, 26934, 26831, 26788, 26788, 26857, 26912, 26781, 26706, 26816, 26714, 26709, 26784, 26590, 26671, 26605, 26625, 26836, 26539, 26668, 26606, 26717, 26639, 26632, 26642, 26559, 26499, 26563, 26417, 26555, 26338, 26617, 26477, 26456, 26642, 26415, 26339, 26483, 26470, 26399, 26468, 26593, 26352, 26354, 26345, 26398, 26378, 26469, 26346, 26372, 26390, 26434, 26306, 26359, 26331, 26390, 26348, 26469, 26168, 26342, 26128, 26258, 26390, 26251, 26268, 26241, 26223, 26395, 25941, 26110, 26293, 26226, 26247, 26183, 26099, 26034, 26139, 26190, 26168, 26268, 26107, 26223, 26137, 26001, 26145, 26052, 25999, 26168, 26038, 26225, 26168, 26057, 26095, 26173, 26094, 25948, 25966, 25999, 25994, 26045, 26114, 25971, 26158, 25913, 26090, 25813, 25930, 25903, 25956, 26020, 26001, 25825, 25939, 25950, 26045, 25925, 26008, 25808, 26013, 25938, 25712, 25883, 25989, 25900, 25794, 25760, 25943, 25783, 25953, 25667, 25756, 25915, 25963, 25824, 25858, 25833, 25905, 25729, 25951, 25770, 25971, 25767, 25859, 25928, 25627, 25767, 25863, 25623, 25772, 25706, 25657, 25806, 25819, 25724, 25712, 25735, 25587, 25726, 25606, 25780, 25597, 25743, 25704, 25615, 25592, 25770, 25735, 25588, 25755, 25680, 25519, 25692, 25737, 25552, 25616, 25639, 25521, 25530};
int sq[MAX_SQ]; //square
bool is_prime[EVE + 1];
static int OFFSET;
//[1, n]
int gao(int n) {
//printf("gao %d\n", n);
int sum = tab[n / EVE];
int bound = n / EVE * EVE + 1;
if (bound > n) return sum;
//printf("sum = %d, bound = %d\n", sum, bound);
/* to-go: [bound, n], |n-bound|<EVE */
memset(is_prime, true, sizeof(is_prime));
if (bound == 1) is_prime[0] = false;
for (int i = 2; i <= n / i; ++i) {
for (int j = max(i, (bound + i - 1) / i) * i; j <= n; j += i) {
is_prime[j - bound] = false;
}
}
for (int i = 1; i < MAX_SQ; ++i) {
for (int j = max(i, (int)(lower_bound(sq, sq + MAX_SQ, bound - sq[i]) - sq)); j < MAX_SQ; ++j) {
int x = sq[i] + sq[j];
if (x > n) break;
if (is_prime[x - bound]) ++sum;
}
}
return sum;
}
int main() {
/* pre-fix */
for (int i = 1; i < MAX; ++i) {
tab[i] += tab[i - 1];
}
for (int i = 1; i < MAX_SQ; ++i) {
sq[i] = i * i;
}
int le, ri;
while (cin >> le >> ri) {
cout << gao(ri) - gao(le - 1) << endl;
}
return 0;
}
- 值得一提的是,你也可以選擇如每
100000 分爲一段的方式,這樣表格會長一些,將有300000000100000=3000 項,或許應該稍微格式化一下,比如每打印10 項換一行,這樣顯得漂亮一些。。。 BTW ,上面有一個小優化,就是考慮i2+[ ]2=t,bound≤t≤n 時,固定i 後,枚舉第二項的大小,我們可以規定第二項≥i ,進一步地,我們可以確定第二項的一個下限,即設第二項爲j ,則至少有i2+j2≥bound ,也就是j2≥bound−i2 ,或者說sq[j]≥bound−sq[i] 。