【随便做做】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,功能为列出当前目录下的文件/目录)!之后 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日 星期一 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]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值