今天,某位粉丝问我这道题怎么做,原题链接。
其实刚看到这道题时,我是吓了一跳的。区间[1,10²⁰]!看来必须用unsigned long long而且要追求速度。
第一步:求S(n),其实就是求n的倒数平方根。
...
#define ULL unsigned long long //只是把unsigned long long改成ULL而已
...
double S(double number){
return (double)(1.0 / sqrt(number));
}
...
第二步:求B(x , y),对y保留x位小数。
对于y保留n位小数,可以考虑对y×10ⁿ进行向下取整然后再除以10ⁿ。
...
#define ULL unsigned long long //只是把unsigned long long改成ULL而已
...
double B(ULL bits , float a){ //B(保留位数,要保留的数)
return floor(a * pow(10 , bits)) / pow(10 , bits);
}
...
第三步:由于Sum(n)里要判断质数(题目里说啦,没看到的再读读题),先写一个判断质数。
...
#define ULL unsigned long long
...
bool ifprime(ULL n){
if(n<=1)
return false;
if(n==2)//2特殊处理
return true;
for(int i=3;i*i<=n;i++){
if(n%i==0)
return false;
}
return true;
}
...
然后再组合一个Sum函数:
double Sum(ULL p , ULL q){
double sum = 0;
for(ULL i = p;i <= q;i++){
if(!ifprime(i))
continue;
sum += B(2 , S(i));
}
return sum;
}
于是1代代码就出来了!
#include <iostream>
#include <cmath>
#define ULL unsigned long long
using namespace std;
double S(double number){
return (double)(1.0 / sqrt(number));
}
double B(ULL bits , float a){
return floor(a * pow(10 , bits)) / pow(10 , bits);
}
bool ifprime(ULL n){
if(n<=1)
return false;
if(n==2)//2特殊处理
return true;
for(int i=3;i*i<=n;i++){
if(n%i==0)
return false;
}
return true;
}
double Sum(ULL p , ULL q){
double sum = 0;
for(ULL i = p;i <= q;i++){
if(!ifprime(i))
continue;
sum += B(2 , S(i));
}
return sum;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ULL n , p[100001] = {0} , q[100001] = {0};
cin >> n;
for(ULL i = 1;i <= n;i++)
cin >> p[i] >> q[i];
for(ULL i = 1;i <= n;i++)
cout << Sum(p[i] , q[i]) << endl;
}
其实以上代码并不难,然而……
惨不忍睹……
经过我反复地读题,我发现了……没错!区间内肯定有重复的数,我们没必要再判断一遍质数,所以我们可以创建ifpme数组来表示是否判断过这个数,ifp数组来保存判断结果是true还是false。同理,一代代码里面的这一行:
sum += B(2 , S(i));
也可以用同样的方法。我们创建ifmemory数组来表示是否算过B(2 , S(i))的值,memory数组来保存B(2 , S(i))的值。这就是——记忆化搜索。
...
double memory[1000001] = {0};
bool ifmemory[1000001] = {false} , ifpme[1000001] = {false} , ifp[1000001] = {false};
...
再对ifprime函数进行一个改编(加入判断)
...
#define ULL unsigned long long
...
bool ifprime(ULL n){
if(n <= 1){
ifpme[n] = true;
ifp[n] = false;
return false;
}
if(n == 2){
ifpme[n] = true;
ifp[n] = true;
return true;
}
for(ULL i = 2;i * i <= n;i++)
if(n % i == 0){
ifpme[n] = true;
ifp[n] = false;
return false;
}
ifpme[n] = true;
ifp[n] = true;
return true;
}
...
对Sum函数也一样:
...
#define ULL unsigned long long
#define mod 1000000
...
double Sum(ULL p , ULL q){
p %= mod , q %= mod;
double sum = 0;
for(ULL i = p;i <= q;i++){
if(ifpme[i] == false){
if(!ifprime(i))
continue;
}
else{
if(ifp[i] == false)
continue;
}
if(ifmemory[i] == false){
sum += B(2 , S(i));
ifmemory[i] = true;
memory[i] = B(2 , S(i));
}
else{
sum += memory[i];
}
}
return sum;
}
...
再组合到一起:
#include <iostream>
#include <iomanip>
#include <cmath>
#define ULL unsigned long long
#define mod 1000000
using namespace std;
double memory[1000001] = {0};
bool ifmemory[1000001] = {false} , ifpme[1000001] = {false} , ifp[1000001] = {false};
double S(double number){
return (double)(1.0 / sqrt(number));
}
double B(ULL bits , float a){
return floor(a * pow(10 , bits)) / pow(10 , bits);
}
bool ifprime(ULL n){
if(n <= 1){
ifpme[n] = true;
ifp[n] = false;
return false;
}
if(n == 2){
ifpme[n] = true;
ifp[n] = true;
return true;
}
for(ULL i = 2;i * i <= n;i++)
if(n % i == 0){
ifpme[n] = true;
ifp[n] = false;
return false;
}
ifpme[n] = true;
ifp[n] = true;
return true;
}
double Sum(ULL p , ULL q){
p %= mod , q %= mod;
double sum = 0;
for(ULL i = p;i <= q;i++){
if(ifpme[i] == false){
if(!ifprime(i))
continue;
}
else{
if(ifp[i] == false)
continue;
}
if(ifmemory[i] == false){
sum += B(2 , S(i));
ifmemory[i] = true;
memory[i] = B(2 , S(i));
}
else{
sum += memory[i];
}
}
return sum;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ULL n , p[100001] = {0} , q[100001] = {0};
cin >> n;
for(ULL i = 1;i <= n;i++)
cin >> p[i] >> q[i];
for(ULL i = 1;i <= n;i++)
cout << Sum(p[i] , q[i]) << endl;
}
这样好了很多!
可是还是有一个TLE……
于是我把目光放到了S(n)上:
由于这道题对精度的要求不高(两位),所以在求S(n)时,可以考虑InvSqrt。(有兴趣了解的点我!)这里我用的是double。
...
double S(double number){
long long i;
double x2, y;
const double threehalfs = 1.5;
x2 = number * 0.5;
y = number;
i = *(long long*)& y;
i = 0x5fe6eb50c7b537a9 - (i >> 1);
y = *(double*) &i;
y = y * (threehalfs - (x2 * y * y));
y = y * (threehalfs - (x2 * y * y));
return y;
}
...
合起来:
#include <iostream>
#include <iomanip>
#include <cmath>
#define ULL unsigned long long
#define mod 1000000
using namespace std;
double memory[1000001] = {0};
bool ifmemory[1000001] = {false} , ifpme[1000001] = {false} , ifp[1000001] = {false};
double S(double number){
long long i;
double x2, y;
const double threehalfs = 1.5;
x2 = number * 0.5;
y = number;
i = *(long long*)& y;
i = 0x5fe6eb50c7b537a9 - (i >> 1);
y = *(double*) &i;
y = y * (threehalfs - (x2 * y * y));
y = y * (threehalfs - (x2 * y * y));
return y;
}
double B(ULL bits , float a){
return floor(a * pow(10 , bits)) / pow(10 , bits);
}
bool ifprime(ULL n){
if(n <= 1){
ifpme[n] = true;
ifp[n] = false;
return false;
}
if(n == 2){
ifpme[n] = true;
ifp[n] = true;
return true;
}
for(ULL i = 2;i * i <= n;i++)
if(n % i == 0){
ifpme[n] = true;
ifp[n] = false;
return false;
}
ifpme[n] = true;
ifp[n] = true;
return true;
}
double Sum(ULL p , ULL q){
p %= mod , q %= mod;
double sum = 0;
for(ULL i = p;i <= q;i++){
if(ifpme[i] == false){
if(!ifprime(i))
continue;
}
else{
if(ifp[i] == false)
continue;
}
if(ifmemory[i] == false){
sum += B(2 , S(i));
ifmemory[i] = true;
memory[i] = B(2 , S(i));
}
else{
sum += memory[i];
}
}
return sum;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ULL n , p[100001] = {0} , q[100001] = {0};
cin >> n;
for(ULL i = 1;i <= n;i++)
cin >> p[i] >> q[i];
for(ULL i = 1;i <= n;i++)
cout << Sum(p[i] , q[i]) << endl;
}
这样就好了!
总结一下,难点就只有后面的记忆化搜索。但我觉得这个代码太长了,而且速度其实一般般(968ms压线),如果有大神有更好的方法,欢迎评论在下方!