AT_abc397_d【题解】

D - Cubes

原题链接

D - Cubes

题意简述

给定一个正整数 N N N,判断是否存在正整数对 ( x , y ) (x,y) (x,y),使得 x 3 − y 3 = N ( N ∈ [ 1 , 1 e 18 ] ) x^{3} - y^{3} = N(N ∈ [1,1e18]) x3y3=N(N[1,1e18])

思路

数据规模提示这题必须用数学方法才能通过所有测试点。

不难想到立方差公式
x 3 − y 3 = ( x − y ) ( x 2 + x y + y 2 ) x^{3} - y^{3} = (x - y) (x^{2} + xy + y^{2}) x3y3=(xy)(x2+xy+y2)
可以考虑根据这个式子枚举 x − y x-y xy


d = x − y d = x - y d=xy
则有
x = d + y x = d + y x=d+y
代入原式,有
x 2 + x y + y 2 = 3 y 2 + 3 d y + d 2 x^{2} + xy + y^{2} = 3y^{2} + 3dy + d^{2} x2+xy+y2=3y2+3dy+d2
也就是说
3 y 2 + 3 d y + d 2 = N d 3y^{2} + 3dy + d^{2}=\frac{N}{d} 3y2+3dy+d2=dN
设正整数 k = N d k = \frac{N}{d} k=dN d d d 是已知常数,将原式展开、整理成关于 y y y 一元二次方程形式:
3 y 2 + 3 d y + d 2 − k = 0 3y^{2} + 3dy + d^{2} - k = 0 3y2+3dy+d2k=0
显然该方程有解

由一元二次方程根的判别式
Δ = b 2 − 4 a c \Delta =\mathop{{b}}\nolimits^{{2}}-4ac \\ Δ=b24ac
对于本题 a = 3 , b = 3 d , c = d 2 − k a=3,b=3d,c=d^{2}-k a=3,b=3d,c=d2k

代入,整理得
Δ = − 3 d 2 + 12 k ≥ 0 \Delta = -3d^{2} + 12k\ge0 Δ=3d2+12k0
又因为 k = N d k = \frac{N}{d} k=dN,解不等式,得
d ≤ 4 N 3 d \le \sqrt[3]{4N} d34N
那么,得出结论:枚举 d d d 的范围为 [ 1 , 4 N 3 ] [1,\sqrt[3]{4N}] [1,34N ]

对于本题极端情况, d ≤ 1587401.052 d\le 1587401.052 d1587401.052 可以通过。

接下来考虑怎么解方程。

由于枚举时 d d d 本身就是正整数,只需考虑 y y y 有无正整数解就可以了,只要找到一个合法的 y y y x x x 也就可以确定,问题解决。

对于原方程,显然有
y = − 3 d + Δ 6 y ={-3d + \sqrt{\Delta}\over 6} y=63d+Δ
先实现一个函数判断 Δ \sqrt\Delta Δ 是不是整数,再判断 y y y 是不是整数就可以了。

综上, O ( 4 N 3 ) O(\sqrt[3]{4N}) O(34N ) 的枚举, O ( 1 ) O(1) O(1) 的判断,本题可以通过。

至于如何求出 4 N 3 \sqrt[3]{4N} 34N ,根据单调性二分 m a x d max_d maxd 即可

注意无解要输出 − 1 -1 1

代码实现

注意在判定 Δ \Delta Δ 是不是平方数时用了二分的方法,主要是 s q r t sqrt sqrt 函数在如此巨大的数据规模下可能会精度丢失,二分更稳健。

为了安全,基本所有变量都使用 unsigned long long,尽管有的不需要。

#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;

ull get_max_d(ull N){
	N <<= 2;//N * 4
	//N开三次方后一定在int范围内,不必开longlong
	ull l = 1,r = 1587402,mid,ans;
	//r没必要像我开的这么极限,当然这样可以防止爆longlong
	while(l <= r){
		mid = l + r >> 1;
		ull d = 1ll * mid * mid * mid;
		if(d <= N){
			ans = mid;
			l = mid + 1;
		}else r = mid - 1;
	}
	return ans;
}

//计算平方根 
long long compute_sqrt(ull del) {
    ull l = 1, r = sqrt(del) + 1;
    while (l <= r) {
        ull mid = (l + r) / 2;
        if (mid * mid == del) return mid;
        else if (mid * mid < del) l = mid + 1;
        else r = mid - 1;
    }
    return -1;
}

int main(){
	ull N;
	cin >> N;
	//二分求解d的最大值
    ull max_d = get_max_d(N);
	//枚举d
	for(int d = 1;d <= max_d;d++){
		
		//d不是N的因子,必无解
		if(N % d) continue;
		 
		ull del = -3ll * d * d + 12 * N / d; 
		ull m = compute_sqrt(del);//判断sqrt(del)是否是整数 
		if (m == -1) continue;
		
		//numerator:分子 
		ull numerator = -3 * d + m;
		if(numerator <= 0) continue;
		if(numerator % 6) continue;
		
		//找到可行解
		ull y = numerator / 6;
		if(y <= 0) continue; 
		ull x = d + y;
		if(x < y) swap(x,y);
		cout << x << " " << y << endl;
		return 0;
	} 
	cout << -1 << endl;
	return 0;
}

好题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值