【隨便做做】Codeforces#86 Problem C Double Happiness

  • 靜態數組內存是受不了的。但同學好機智,用vector 強行開150000000 的數組,水了過去。。。
  • f(x) 表示範圍[1,x] 內滿足題意的t 的個數,那麼
    ans=f(r)f(l1)
    我們考慮打表求解,顯然,不可以直接打表,所以我們考慮將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日 星期一 213546*********************************************** */
#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.cpp

  • Windows 也類似,注意確認是否已手動將g++.exe添加進環境變量,否則cmd下直接運行g++是無法找到該程序的。具體請自行百度之。。。

    g++ c_gen.cpp -o c_gen -std=c++11
    c_gen >> c.cpp

    注意二者的區別是運行生成的c_gen的方式不同,Linux 直接輸入命令是隻在$PATH環境變量中包含的路徑查找,並且不包含當前目錄。試想,由於Linux 下任何用戶都有/tmprwx權限,一個普通用戶在裏面放置了一個惡意程序,甚至命名爲ls(ls原本爲系統程序,位於/bin/ls,功能爲列出當前目錄下的文件/目錄)!之後roottmp中想查看文件,然後執行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日 星期一 213552*********************************************** */
#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,boundtn 時,固定i 後,枚舉第二項的大小,我們可以規定第二項i ,進一步地,我們可以確定第二項的一個下限,即設第二項爲j ,則至少有i2+j2bound ,也就是j2boundi2 ,或者說sq[j]boundsq[i]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章