背景介绍
佩尔方程,是一种不定二次方程。Pell方程,古希腊和印度的数学家对此类方程的研究做了最早的贡献,由费马首先进行了深入研究,拉格朗日给出了解决方案,但后此类方程来却被欧拉误记为佩尔提出,并写入他的著作中。后人多称佩尔方程。沿续至今。
内容简介
佩尔方程是一种不定二次方程。就是如下形式的方程
x2 - dy2 = 1;
1),如果d为平方数,则方程变为了(x-√dy)(x+√dy) = 1,此时只有x = 1或-1,y=0,这两组特定解。
2),当d不为平方数时,方程有无穷多组整数解。
求解最小整数解
已知x2-dy2 = 1,
假设(x1, y1), (x2, y2),满足上面的式子,带入后得到
x 1 2 x^2_ {1} x12 - d * y 1 2 y^2_ {1} y12 = 1
x 2 2 x^2_ {2} x22 - d * y 2 2 y^2_ {2} y22 = 1
将上面两个式子相乘后得到
x 1 2 x^2_ {1} x12 * x 2 2 x^2_ {2} x22 - d x 1 2 x^2_ {1} x12 y 2 2 y^2_ {2} y22 -d y 1 2 y^2_ {1} y12 x 2 2 x^2_ {2} x22 +d2 y 1 2 y^2_ {1} y12 y 2 2 y^2_ {2} y22 = 1
整理后得到
[(x1x2)2 + (dy1y2)2] - d[(x1y2)2 + (x2y1)2] = 1
对左边和右边同时加上 2dx1y1x1y2后变为了如下形式
(x1x2 + dy1y2)2 - d(x1y2 + x2y1)2 = 1
神奇的发现它还是一个佩尔方程这个时候
x = x1x2 + dy1y2
y = x1y2 + x2y1
所以也就得到了一个递推式
xn = xn-1x1 + dy1yn-1
yn = x1yn-1 + xn-1y1
进一步变换后就能变为了下面这个矩阵,所以再求某个特解的时候可以用矩阵快速幂
所以我们只要求得最小解后,就能求得所有的解。
求最小解有两种方法,连分数和暴力,我现在还没太看懂连分数,后期看懂再补。
#include <iostream>
#include <cmath>using namespace std;int main()
{int d;int y = 1, x = 0;cin >> d;while (1){x = sqrt(1+d*pow(y, 2));if (x*x - d*y*y == 1){break;}y++;}cout << x << " " << y << endl;return 0;
}
hdu6222
接下来就到了例题了
题目地址:
http://acm.hdu.edu.cn/showproblem.php?pid=6222
题目大意:
一个三角形三边满足t-1, t, t+1,就称为什么什么的,现在他给你一个最小的n,让你找到大于等于这个n的t。
解题思路:
求三角形面积用海伦公式来求:
我们设a = t-1, b = t, c = t+1
所以
带入海伦公式后得到
令x = t/2,得到
对两边同时平方后得到
因为s为整数,所以我们能知道x2-1=3*y2,整理后正好是我们今天的佩尔方程。
在看题目给的数据
当题目n等于1时,我们很轻松就得到了最小的t就是4,我们又知道x = t/2,所以最小的t对应最小的解,即x = 2,接着得出y=1 这便是上面佩尔方程的最小解。通过打表就能看到,当n在大于100的时候,就已经比1030次方大了,所以可以把这些解存下来,也可以每次使用矩阵快速幂。
因为数据比较大,所以使用_ _int128来做题。
#include <iostream>using namespace std;typedef __int128 _int;_int num[105][2];void read(_int &x)
{char ch;int flag = 1;x = 0;while (ch = getchar()){if (ch == '-'){flag = -1;}else if (ch>='0' && ch<='9'){break;}}x = ch - '0';while ((ch = getchar())>='0' && ch <= '9'){x = x*10 + ch-'0';}x*=flag;
}void out(_int x)
{if (x<0){x = -x;putchar('-');}if (x>=10){out(x/10);}putchar(x%10+'0');
}int main()
{num[1][0] = 2, num[1][1] = 1;for (int i=2; i<=100; i++){num[i][0] = num[i-1][0]*num[1][0] + 3*num[i-1][1]*num[1][1];num[i][1] = num[i-1][1]*num[1][0] + num[1][1]*num[i-1][0];} int n;cin >> n;while (n--){_int temp = 0;read(temp);for (int i=1; i<=100; i++){if (num[i][0] * 2 >= temp){out(num[i][0]*2);puts("");break;}}}return 0;
}