[CSP-J 2022] 解密
题目描述
给定一个正整数 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
样例输入 #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 |
无 |
题目分析
本题要求p与q的值。
已知的两个信息
- n=p×q
- e×d=(p−1)(q−1)+1
展开式子2:e×d=p×q−(p+q)+2=n−(p+q)+2
那么可得:p+q=n−e×d+2
整理一下:{p×qp+q=n=n−e×d+2=m
n和m再输入后已知,那么本题就是求一元二次方程解。
根据式子1:q=pn
带入式子2: p+pn=m
两边相乘并调整下位置:p2−mp+n=0
求解p的值即可。
p=2a−b±b2−4ac
此时 a=1,b=−m,c=n
若 b2−4ac<0 则无解。
由于p为整数,所以若b±b2−4ac 无法整除 2a 也是无解。
其它则代入公式进行计算即可。
代码实现
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
ll n,d,e;
//p^2 -(n-e*d+2)p+n = 0
bool chk(ll num){//判断num是否时完全平方数
ll t=ll(sqrt(num));
return t*t==num;
}
int main(){
int k;
cin>>k;
while(k--){
cin>>n>>d>>e;
ll b=e*d-n-2;
ll a=1,c=n;
if(b*b<4*a*c){
cout<<"NO"<<endl;
}else{
ll t=b*b-4*a*c;
bool f=0;
if(chk(t)&&(ll(-b+sqrt(t))%(2*a)==0)){
ll p=(-b-sqrt(t))/(2*a);
ll q=n/p;
if(p>=1){
f=1;
cout<<p<<" "<<q<<endl;
}
}
if(!f) cout<<"NO"<<endl;
}
}
return 0;
}