pongo(csdn英雄会题解)之三元组的数量--英雄会第二届在线编程大赛·CSDN现场决赛

/*{5 3 1}和{7 5 3}是2组不同的等差三元组,除了等差的性质之外,还有个奇妙的地方在于:5^2 – 3^2 – 1^2 = 7^2 – 5^2 – 3^2 = N = 15。
{19 15 11}同{7 5 3}这对三元组也存在同样的性质:19^2 – 15^2 – 11^2 = 7^2 – 5^2 – 3^2 = N = 15。
这种成对的三元组还有很多。当N = 15时,有3对,分别是{5 3 1}和{7 5 3},{5 3 1}和{19 15 11},{7 5 3}和{19 15 11}。

现给出一个区间 [a,b]求a <= N <= b 范围内,共有多少对这样的三元组。(1 <= a <= b <= 5*10^6)
例如:a = 1,b = 30,输出:4。(注:共有4对,{5 3 1}和{7 5 3},{5 3 1}和{19 15 11},{7 5 3}和{19 15 11},{34 27 20}和{12 9 6})
*/
#include <stdio.h>
#include <iostream>
#include <string>
#include <cmath>
#include <ctime>
#include <vector>
#include <map>
using namespace std;
//x, x+d, x+2d   N
//等差三元组中x>0 d>0
//(x+2d)^2-(x+d)^2-x^2=-x^2+2dx+3d^2=(3d-x)(x+d)=N;
//另设p=3d-x ;   q=x+d; 那么得:p*q=N  ;   d=(p+q)/4; x=(3q-p)/4;
//1) p+q能被4整除;
//2) 3q-p大于等于4(q>=p/3+2);(条件1、2能推出3q-p能被4整除:因为3q-p+p+q=4p)
//3) a=<p*q<=b
//4)p,q是正整数
#define GetN (long long(d)*(2*x+3*d)-x*x)
#define MAX 50000000
static int *count_map;
static int *count_map2;
class Test {
public:
	static map<vector<int,int>,int> stored_map;
	
	static void create_map(){//方法一:因式分解
		int p,q;
		count_map = new int[MAX+1];
		for(int i=0;i<=MAX;++i) count_map[i]=0;
		for(p=1;p<=MAX;++p){
			q=4-p%4;
			int p3 = p/3+2; //3*q-p>=4转化,防止while内多次计算
			while(q<=MAX){
				int temp = p*q;
				if(temp>MAX) break;
				if(q>=p3) {
					++count_map[temp];
				}
				q+=4;
			}
			
		}
	}
	static void create_map2(){/*方法二:解方程 2*x*d+3*d^2-x^2=N =>
		d^2 - (x/2-d/2)^2 = N > d^2-(d-0.5)^2 => d<=N/4+1
		其中x = d + sqrt(4*d^2-N)或 d-sqrt(4*d^2-N)
		由此d从1到N/4+1循环,delta = sqrt(4*d^2-N)从最小值max(0,sqrt(4*d^2-Nmax))
		到2*d循环进行最大范围的统计,不同delta和d组成不同的x和d,也就是不同的三元组
		时间复杂度小于n^2,和nlog(n)比应该比较接近*/
		int d;				
		int x;				 
		count_map2 = new int[MAX+1];
		for(int i=0;i<=MAX;++i) count_map2[i]=0;
		for(d=1;d<=MAX/4+1;++d){
			long long d4_2 = (long long)4*d*d; //d4_2 = 4*d^2
			int delta=0;
			if(d4_2>MAX)
				delta = sqrt((double)(d4_2-MAX));  
			int d2 = 2*d;
			while(delta<d2){
				long long delta_2 =(long long)delta*delta;
				int n = d4_2 - delta_2;
				if(n<=MAX){ 
					if(delta>0&&d-delta>0) ++count_map2[n];
					++count_map2[n];
				}
				++delta;
			}
		}
	}
	static int Count (int   a,int   b)
	{
		static bool first = true;
		if(first){
			first=false;
			clock_t t1=clock();
			create_map();
			clock_t t2=clock();
			std::cout<<(t2-t1)/(double)CLOCKS_PER_SEC<<" s"<<std::endl;
			cout<<"map2:"<<endl;
			t1=clock();
			create_map2();
			t2=clock();
			std::cout<<(t2-t1)/(double)CLOCKS_PER_SEC<<" s"<<std::endl;
		}
		int ret = 0;
		for(int i=a;i<=b;++i){
			ret+=count_map[i]*(count_map[i]-1);
			//if(count_map[i]!=count_map2[i]) cout<<i<<":"<<count_map[i]<<","<<count_map2[i]<<endl;
		}
		return ret/2;
	}
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{   
	int a,b;
	while(1){
		cin>>a>>b;
		cout<<Test::Count(a,b)<<endl;   
	}
} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。


//开始用解方程的方法想一个n一个n求,发现超时,改成所有范围内的数一次求完并做记录,以后只要查询就直接获得结果,这样就没有超三秒了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值