倒数平方根之和(U244730)——记忆化搜索的应用

今天,某位粉丝问我这道题怎么做,原题链接

 

其实刚看到这道题时,我是吓了一跳的。区间[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压线),如果有大神有更好的方法,欢迎评论在下方! 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值