**摘要:**本题给定 k 次询问,每次给出三个正整数 n、e、d,要求求出两个正整数 p、q,使其满足 n=p×q 且 e×d=(p−1)(q−1)+1。通过数学推导可将问题转化为求解一元二次方程,利用判别式判断是否有整数解,并借助二分或开方运算高效求解,最终输出满足条件的 p、q 或 NO。
题目描述
给定一个正整数 k,有 k 次询问,每次给定三个正整数 ni,ei,di,求两个正整数 pi,qi,使 ni=pi×qi、ei×di=(pi−1)(qi−1)+1。
输入格式
第一行一个正整数 k,表示有 k 次询问。
接下来 k 行,第 i 行三个正整数 ni,di,ei。
输出格式
输出 k 行,每行两个正整数 pi,qi 表示答案。
为使输出统一,你应当保证 pi≤qi。
如果无解,请输出 NO。
输入输出样例
输入 #1复制
10
770 77 5
633 1 211
545 1 499
683 3 227
858 3 257
723 37 13
572 26 11
867 17 17
829 3 263
528 4 109
输出 #1复制
2 385
NO
NO
NO
11 78
3 241
2 286
NO
NO
6 88
说明/提示
【样例 #2】
见附件中的 decode/decode2.in 与 decode/decode2.ans。
【样例 #3】
见附件中的 decode/decode3.in 与 decode/decode3.ans。
【样例 #4】
见附件中的 decode/decode4.in 与 decode/decode4.ans。
【数据范围】
以下记 m=n−e×d+2。
保证对于 100% 的数据,1≤k≤105,对于任意的 1≤i≤k,1≤ni≤1018,1≤ei×di≤1018 ,1≤m≤109。
| 测试点编号 | k≤ | n≤ | m≤ | 特殊性质 |
|---|---|---|---|---|
| 1 | 103 | 103 | 103 | 保证有解 |
| 2 | 103 | 103 | 103 | 无 |
| 3 | 103 | 109 | 6×104 | 保证有解 |
| 4 | 103 | 109 | 6×104 | 无 |
| 5 | 103 | 109 | 109 | 保证有解 |
| 6 | 103 | 109 | 109 | 无 |
| 7 | 105 | 1018 | 109 | 保证若有解则 p=q |
| 8 | 105 | 1018 | 109 | 保证有解 |
| 9 | 105 | 1018 | 109 | 无 |
| 10 | 105 | 1018 | 109 | 无 |
附件下载
decode.zip1.74KB
代码实现:
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int k;
scanf("%d", &k);
while(k--){
ll n, d, e;
scanf("%lld %lld %lld", &n, &d, &e);
ll m = n - e * d + 2;
__int128 delta = (__int128)m * m - (__int128)4 * n;
if(delta < 0){
printf("NO\n");
continue;
}
ll s = (ll)sqrtl((long double)delta);
while((__int128)s * s < delta) s++;
while((__int128)s * s > delta) s--;
if((__int128)s * s != delta){
printf("NO\n");
continue;
}
if((m - s) % 2 != 0 || (m - s) <= 0){
printf("NO\n");
continue;
}
ll p = (m - s) / 2;
ll q = (m + s) / 2;
if(p <= 0 || q <= 0 || p * q != n){
printf("NO\n");
continue;
}
printf("%lld %lld\n", p, q);
}
return 0;
}