2021牛客暑期多校训练营3 E. Math(数论/韦达定理)
链接:https://ac.nowcoder.com/acm/contest/11254/E
来源:牛客网
题目描述
Given n, count the number of pairs of positive integers (x, y), such that xy+1∣x2+y2,1≤x≤y≤nxy+1∣x2+y2,1≤x≤y≤n.
There're t test cases in total.
输入描述:
First line contains an integer t(1≤t≤105)t(1≤t≤105). Next t lines each contains one integer n (1≤n≤1018)(1≤n≤1018).
输出描述:
Output t lines, denoting the answer of each testcase.
示例1
输入
复制
10
10
100
1000
10000
100000
114514
1919810
20190104
123123123123
10000001000000
输出
复制
2
5
14
31
65
67
158
326
5226
22091
看了dls的题解悟了半天貌似懂了(有错请指正
当然这个题可以打表找规律(找到了但没完全找到)。
由\(xy+1|x^2+y^2\),不妨设\(k(xy+1)= x^2 + y^2\ 且\ x\leq y\)。如果固定x不变,可以将方程写为:\(y^2-kxy+x^2-k=0\)。由高中学过的韦达定理,这个方程的两个解为\(y_1 = \frac{kx+\sqrt {k^2x^2-4(x^2-k)}}{2}, y_2 = \frac{kx-\sqrt {k^2x^2-4(x^2-k)}}{2}\)。因此\(y_1 + y_2 = kx, y_1 y_2=x^2-k\)。不妨设\(y_1\)是一开始那个解\(y\),则\(y_2\)这个解更小(因为\(y_1 > x\)且\(y_1 y_2 = x^2-k\),若\(y_2\)更大则显然不可能)且非负(如果是负数,带入原方程会矛盾)。所以此时有\(k(xy_2+1)= x^2 + y_2^2\ 且\ y_2\leq x \ 且\ y_2=kx-y1\)。这时候把\(y_2\)看做\(x\),\(x\)看做\(y\)(统一成原来方程的形式),就可以发现这样可以不断进行递降,且由一开始\((x, y)\)这组解递降得到的解\((kx-y, x)\)中\(kx-y\leq x\)。因此对于这个k,绝对值最小的一组正整数解一定满足\(kx-y=0\)(因为要求是正整数解,而0到\(y\)的正整数个数是有限的,不是0的话就没有出口了)。此时\(y=kx\)(注意这里的x和y并不是一开始的,仅仅是描述最终的解的形式),带入原方程得到\(x^2+k^2x^2=k^2x^2+k\),即\(x^2=k\)。因此设最终那组解里最小的数为\(x\),则\(y = kx=x^2x=x^3\),解为\((x,x^3)\)。由于这个解是递降得到的,因此可以逆推回去。设\(x=kx'-y',y=x'\)(这里的y是\(x^3\),\(k\)是\(x^2\)不变),则前一组解\((x', y')=(x^3,x^5-x)\)。这样不断倒推回去就能得到由一个初始的x推出来的所有的解了。对于这个题而言,需要进行预处理,预处理的时候枚举的是绝对值最小的一组正整数解的x,然后对于所有的解\((x, y)\),把\(y\)放入一个vector并排序,对于每次询问直接在集合中upper_bound二分查找n,则这个位置之前的数的个数就是答案。因为1e18开三次根为1e6,枚举的时候枚举到1e6即可。
#include <bits/stdc++.h>
using namespace std;
//int128
int main() {
vector<long long> b;
b.push_back(1ll);
for(long long xx = 2; xx <= 1000000; xx++) {
//cout << xx << endl;
long long x = 1ll * xx, y = 1ll * xx * xx * xx, k = xx * xx;
while(true) {
b.push_back(y);
if(__int128(k) * __int128(y) - __int128(x) > (1e18+5)) {
break;
}
long long t = y;
y = k * y - x;//倒推回去的下一个解的y
x = t;
}
}
sort(b.begin(), b.end());
int t;
cin >> t;
while(t--) {
//cout << t << endl;
long long n;
cin >> n;
long long pos = upper_bound(b.begin(), b.end(), n) - b.begin();
cout << pos << endl;
}
return 0;
}