洛谷 P5596 【XR-4】题
题目描述
小 X 遇到了一道题:
给定自然数 a,ba,b,求满足下列条件的自然数对 (x,y)(x,y) 的个数:
y^2 - x^2 = ax + by2−x2=a**x+b
他不会,只好求助于精通数学的你。
如果有无限多个自然数对满足条件,那么你只需要输出 inf
即可。
输入格式
一行两个整数 a,ba,b。
输出格式
如果个数有限,一行一个整数,表示个数。
如果个数无限,一行一个字符串 inf
。
输入输出样例
输入 #1复制
输出 #1复制
输入 #2复制
输出 #2复制
输入 #3复制
输出 #3复制
输入 #4复制
输出 #4复制
输入 #5复制
输出 #5复制
说明/提示
【样例 1 说明】
(x,y) = (6,9)(x,y)=(6,9)
本题采用捆绑测试。
- Subtask 1(3 points):a = b = 0a=b=0。
- Subtask 2(6 points):0 le a,b le 20≤a,b≤2,不存在无限个数的情况。
- Subtask 3(9 points):0 le a,b le 1000≤a,b≤100,不存在无限个数的情况。
- Subtask 4(13 points):0 le a,b le 10^30≤a,b≤103,不存在无限个数的情况。
- Subtask 5(14 points):0 le a le 10^40≤a≤104,0 le b le 10^70≤b≤107。
- Subtask 6(14 points):a = 0a=0。
- Subtask 7(14 points):b = 0b=0。
- Subtask 8(27 points):无特殊限制。
对于 100%100% 的数据,0 le a le 10^80≤a≤108,0le b le 10^{15}0≤b≤1015。
题解:
这篇题解即将刷新本蒟蒻写的最认真的数学题解的记录。
蒟蒻太菜了考试的时候只拿了18分
蒟蒻一开始的时候是这么想的:
观察原方程:
把它转化成函数形式:
因为(x,yin N),所以我一下子就想到了完全平方数这个东西。
如果能把右侧的部分换成这样的形式:
那就显然会有无数组解。
然后我们展开,变成:
和上面的对应上,就能列出一个等式关系:
当(2k=a,k^2=b)的时候,有无数组解。
一联立:
这个时候有无数组解,输出(inf)即可。
然后蒟蒻就没有思路了。
冥思苦想了很长时间,最后还是用了暴力枚举,剪枝还剪错了,只好用了最最暴力的两重循环枚举来解决了这个问题。只得了18分。
今天看到了正解,惊喜地发现,我和大佬们的思路竟然重合了那么(1\%),至少我推出来了无解的情况应该是什么样子的,而这种情况正是通往“有解”的情况的大门。
我们这样考虑:
因为原式子是个二元二次方程。这个方程很难搞。
如果我们把它换为一元方程的话,肯定就会容易一些。
但是我们还要求可行解的个数...怎么办呢?
灵光一闪(希望大家记住这个思想)
我们可以构造(x,y)的映射关系,这样求出一个合法的(x),肯定就会有一个合法的(y)与之对应,我们就可以少搞很多东西。
那么,我们以此入手,分析原式子可得两边均大于等于0.
我们怎么对这个式子进行处理呢?有一个方法被蒟蒻起了个名字:归一法,这个方法通俗一点讲,就是把一个主元用一些辅元表示,然后通过一些辅元的关系和范围等求出主元的解。
那么我们就可以设(t=y-x,tin N).
这样,原式子就被我们搞成了这样:
整理:
再整理:
那么,在有解的范围内(也就是(a/2 ot= sqrt{b})):
分类讨论:
一、(frac{a}{2}<sqrt{b})时,有:
二、(sqrt{b}<frac{a}{2})时,有:
注意特判分母不得零的情况!!(化身数学班主任)
因为这个(t)定义的时候就是关于(x,y)的式子,所以当我们找到一个合法的(t),我们就找到了一组解((x,y))。
基于此,我们只需要在(t)的范围内循环找可行解即可。
最后强调,代码实现的坑点是取整和特判...
代码:
#include<cmath>
#include<cstdio>
#define int long long
using namespace std;
int a,b,l,r,ans,n;
signed main()
{
scanf("%lld%lld",&a,&b);
r=sqrt(b);
l=a/2;
if(r*r==b && r*2==a)
{
printf("inf");
return 0;
}
else if(a==1 && b==0)
{
printf("1");
return 0;
}
else if(r>=l)
{
for(int i=l+1;i<=r;i++)
if((abs(b-i*i))%(abs(2*i-a))==0)
ans++;
printf("%lld",ans);
return 0;
}
else
{
if(l*2==a)
n=l-1;
else
n=l;
for(int i=r;i<=n;i++)
if((abs(b-i*i))%(abs(2*i-a))==0)
ans++;
printf("%lld",ans);
return 0;
}
}