您的位置:首页 > 移动开发

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

2015-09-07 22:18 267 查看
静态数组内存是受不了的。但同学好机智,用vectorvector强行开150000000150000000的数组,水了过去。。。

令f(x)f(x)表示范围[1,x][1,x]内满足题意的tt的个数,那么ans=f(r)−f(l−1)ans=f(r)-f(l-1)我们考虑打表求解,显然,不可以直接打表,所以我们考虑将33亿分段,每10000001000000分一段,于是总共需要预处理出300300个数,即f(0),f(1000000),f(2000000),…,f(300000000)f(0),f(1000000),f(2000000),\dots,f(300000000)这在本地是可以O(n)O(n)地得到的(EularEular筛法O(n)O(n)+枚举完全平方数O(n)O(n)),然后对一个询问f(x)f(x),只需要在线求解xx%1000000这一部分即可。

我们举例说明求解f(11111111)f(11111111)的方法,查上述规模为300300的表格,我们发现有f(11000000)f(11000000)可用,也就是已知[1,11000000][1,11000000]内满足题意的数的个数;接下来,只需要求解范围[11000001,11111111][11000001,11111111]内满足题意的个数即可,由于我们每10000001000000做一个分割,因此我们真正需要在线做的这部分规模不会大于10000001000000,就可以使用你喜欢的筛素数方法通过了。为了简洁,后面这部分窝就直接O(nn−−√)O(n\sqrt n)地做了。

本地离线预处理部分:

/* **********************************************

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;
}


LinuxLinux下可在终端用下述命令将打表结果添加到
c.cpp
后面,避免手动粘贴疏漏

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

./c_gen >> c.cpp

WindowsWindows也类似,注意确认是否已手动将
g++.exe
添加进环境变量,否则
cmd
下直接运行
g++
是无法找到该程序的。具体请自行百度之。。。

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

c_gen >> c.cpp

注意二者的区别是运行生成的
c_gen
的方式不同,LinuxLinux直接输入命令是只在
$PATH
环境变量中包含的路径查找,并且不包含当前目录。试想,由于LinuxLinux下任何用户都有
/tmp
rwx
权限,一个普通用户在里面放置了一个恶意程序,甚至命名为
ls
(
ls
原本为系统程序,位于
/bin/ls
,功能为列出当前目录下的文件/目录)!之后rootroot在
tmp
中想查看文件,然后执行
ls
。。。后果可想而知。。。也正因此,LinuxLinux执行当前目录下的程序需要输入
./程序名
,相当于要强调“俺要运行当前目录下那个啥啥啥。。。”

扯远了。。。总之上述完成后,我们就得到想要的表了,当然,窝打的表是分段的,拿来用之前需要先处理出它的前缀和,即打的表统计的是[1,1000000],[1000001,2000000],…[1,1000000],[1000001,2000000],\dots各段的数量,我们真正需要的是比如[1,3000000][1,3000000]这样的形式,于是相当于需要前33项的和,又如[1,11000000][1,11000000]需要的是前1111项的和,随便一提,前缀和就是前若干项和。。。

然后就是下面的处理

/* **********************************************

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;
}


值得一提的是,你也可以选择如每100000100000分为一段的方式,这样表格会长一些,将有300000000100000=3000{300000000\over100000}=3000项,或许应该稍微格式化一下,比如每打印1010项换一行,这样显得漂亮一些。。。

BTWBTW,上面有一个小优化,就是考虑i2+[ ]2=t,bound≤t≤ni^2+[\ \ ]^2=t,bound\le t\le n时,固定ii后,枚举第二项的大小,我们可以规定第二项≥i\ge i,进一步地,我们可以确定第二项的一个下限,即设第二项为jj,则至少有i2+j2≥boundi^2+j^2\ge bound,也就是j2≥bound−i2j^2\ge bound - i^2,或者说sq[j]≥bound−sq[i]sq[j]\ge bound - sq[i]。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: