前言:
比赛的时候想出了三种做法,暴力,二分还有数学。额,实测暴力70pts
,二分100pts
,数学100pts
。但是比赛的时候这道题还是保龄了(我才不会告诉你我,在这写下题解算是警戒和纪念吧(freopen
调试的时候加的注释没有删掉呢纪念保龄。这里介绍暴力,二分,数学三种做法,不过二分的代码我给删了,先放一下其他大佬的代码吧(当然是照着敲一遍的,我才懒得找人家说明情况
题目:
Luogu:Link
简要:
给定一个正整数 k k k,有 k k k 次询问,每次给定三个正整数 n i , e i , d i n_i, e_i, d_i ni,ei,di,求两个正整数 p i , q i p_i, q_i pi,qi,使 n i = p i × q i n_i = p_i \times q_i ni=pi×qi、 e i × d i = ( p i − 1 ) ( q i − 1 ) + 1 e_i \times d_i = (p_i - 1)(q_i - 1) + 1 ei×di=(pi−1)(qi−1)+1。
输入格式
第一行一个正整数 k k k,表示有 k k k 次询问。
接下来 k k k 行,第 i i i 行三个正整数 n i , d i , e i n_i, d_i, e_i ni,di,ei。
输出格式
输出 k k k 行,每行两个正整数 p i , q i p_i, q_i pi,qi 表示答案。
为使输出统一,你应当保证 p i ≤ q i p_i \leq q_i 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
Step 1 暴力:
额,暴力做法非常简单,照着题意直接敲就好了。用两层For
循环分别枚举p
和q
的话肯定会T
得很惨,那么考虑优化。
我们不妨来看看p
和q
还必须满足什么条件(前置知识:整式的乘除)。
我们已知:
e i × d i = ( p i − 1 ) ( q i − 1 ) + 1 ; e_i \times d_i = (p_i - 1)(q_i - 1) + 1 ; ei×di=(pi−1)(qi−1)+1;
展开就是:
e i × d i = p i × q i − p i − q i + 1 + 1 ; e_i \times d_i = p_i \times q_i - p_i - q_i + 1 + 1 ; ei×di=pi×qi−pi−qi+1+1;
化简可得:
e i × d i = p i × q i − ( p i + q i ) + 2 ; e_i \times d_i = p_i \times q_i - (p_i + q_i) + 2 ; ei×di=pi×qi−(pi+qi)+2;
题目中说:
n i = p i × q i ; n_i = p_i \times q_i ; ni=pi×qi;
将 $ n_i $ 代入式中可得:
e i × d i = n i − ( p i + q i ) + 2 ; e_i \times d_i = n_i - (p_i + q_i) + 2 ; ei×di=ni−(pi+qi)+2;
稍稍变化一下,可以得出:
p i + q i = n i − e i × d i + 2 ; p_i + q_i = n_i - e_i \times d_i + 2 ; pi+qi=ni−ei×di+2;
怎么样?是不是发现题目中的变量m
的范围有用了?这也就证明我们的做法是正确的。其实到这里我们已经很接近正解了,不过呢如果为了节省时间,现在已经可以敲代码了!
代码:
Link#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
int t;
int n;
int m;
int k;
int l;
int r;
int s;
inline int read(){
int s=0;
int w=1;
char ch;
ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
w=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<1)+(s<<3)+(ch^48);
ch=getchar();
}
return s*w;
}
inline void write(int s){
if(s<0){
putchar('-');
s=~s+1;
}
if(s>9){
write(s/10);
}
putchar(s%10+'0');
return ;
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
t=read();
while(t--){
n=read();
m=read();
k=read();
l=n-m*k+2;
bool a=false;
for(int i=1;i<=n-m*k+2;i++){
int j=n-m*k+2-i;
if(i*j==n&&((i-1)*(j-1)+1==m*k)){
write(min(i,j));
putchar(' ');
write(max(i,j));
putchar('\n');
a=true;
break;
}
}
if(a==false){
cout<<"NO\n";
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
实测上面代码赛中70pts
,Luogu50pts
。
Step2 数学:
额,按照暴力做法继续向下推:
上次写到哪了?嗷对,写到 p i + q i p_i + q_i pi+qi了,就是这个式子:
p i + q i = n i − e i × d i + 2 ; p_i + q_i = n_i - e_i \times d_i + 2 ; pi+qi=ni−ei×di+2;
我们是不是还有 p i × q i p_i \times q_i pi×qi?
p i × q i = n ; p_i \times q_i = n; pi×qi=n;
连立上面的两个式子(即 $p_i + q_i = n_i - e_i \times d_i + 2 $ 与 p i × q i = n p_i \times q_i = n pi×qi=n)可得:
p i − q i = ± ( p i + q i ) 2 − 4 × p i × q i ; p_i - q_i = \pm \sqrt{ (p_i + q_i) ^ 2 - 4 \times p_i \times q_i }; pi−qi=±(pi+qi)2−4×pi×qi;
再将我们已经得出的 p i + q i p_i + q_i pi+qi和 p i × q i p_i \times q_i pi×qi代入式子中得出:
p i − q i = ± ( n i − e i × d i + 2 ) 2 − 4 × n i p_i - q_i = \pm \sqrt{ (n_i - e_i \times d_i + 2) ^ 2 - 4 \times n_i } pi−qi=±(ni−ei×di+2)2−4×ni
整理可得:
p i − q i = ± ( n i − e i × d i ) 2 + 4 × n i − 4 × e i × d i + 4 − 4 × n i ; p_i - q_i = \pm \sqrt{ (n_i - e_i \times d_i) ^ 2 + 4 \times n_i - 4 \times e_i \times d_i + 4 - 4 \times n_i}; pi−qi=±(ni−ei×di)2+4×ni−4×ei×di+4−4×ni;
整理可得:
p i − q i = ± n i 2 + e i 2 × d i 2 − 2 × n i × e i × d i + 4 × n i − 4 × e i × d i + 4 − 4 × n i g ; p_i - q_i = \pm \sqrt{ n_i ^ 2 + e_i ^ 2 \times d_i ^ 2 - 2 \times n_i \times e_i \times d_i + 4 \times n_i - 4 \times e_i \times d_i + 4 - 4 \times n_ig}; pi−qi=±ni2+ei2×di2−2×ni×ei×di+4×ni−4×ei×di+4−4×nig;
整理可得:
p i − q i = ± n i 2 + e i 2 × d i 2 − 2 × n i × e i × d i − 4 × e i × d i + 4 ; p_i - q_i = \pm \sqrt{ n_i ^ 2 + e_i ^ 2 \times d_i ^ 2 - 2 \times n_i \times e_i \times d_i - 4 \times e_i \times d_i + 4}; pi−qi=±ni2+ei2×di2−2×ni×ei×di−4×ei×di+4;
很好,现在我们有了 p i + q i p_i + q_i pi+qi 与 p i − q i p_i - q_i pi−qi,不就能求 p i p_i pi和 q i q_i qi了么:
p i = n i − e i × d i + 2 − n i 2 + e i 2 × d i 2 − 2 × n i × e i × d i − 4 × e i × d i + 4 2 ; p_i = \frac{n_i - e_i \times d_i + 2 - \sqrt{ n_i ^ 2 + e_i ^ 2 \times d_i ^ 2 - 2 \times n_i \times e_i \times d_i - 4 \times e_i \times d_i + 4}}{2}; pi=2ni−ei×di+2−ni2+ei2×di2−2×ni×ei×di−4×ei×di+4;
q i = n i − e i × d i + 2 + n i 2 + e i 2 × d i 2 − 2 × n i × e i × d i − 4 × e i × d i + 4 2 ; q_i = \frac{n_i - e_i \times d_i + 2 + \sqrt{ n_i ^ 2 + e_i ^ 2 \times d_i ^ 2 - 2 \times n_i \times e_i \times d_i - 4 \times e_i \times d_i + 4}}{2}; qi=2ni−ei×di+2+ni2+ei2×di2−2×ni×ei×di−4×ei×di+4;
这样的 p i p_i pi和 q i q_i qi就能满足 p i p_i pi比 q i q_i qi小了。
额,得出来的 p i p_i pi和 q i q_i qi若不满足题目要求就是无解的情况啦!
代码:
Link#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
int t;
int n;
int m;
int k;
int l;
int r;
int s;
int p;
int q;
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>t;
while(t--){
cin>>n>>m>>k;
l=n-m*k+2;
p=(l-sqrt(l*l-4*n))/2;
q=(l+sqrt(l*l-4*n))/2;
if(p*q==n||((p-1)*(q-1)+1)==m*k){
cout<<p<<" "<<q<<"\n";
}
else{
cout<<"NO\n";
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
Step3 二分:
二分解法是从暴力的思路继续向下推导的:
话说上面写到了这个式子:
p i + q i = n i − e i × d i + 2 ; p_i + q_i = n_i - e_i \times d_i + 2 ; pi+qi=ni−ei×di+2;
也就是 p i + q i p_i + q_i pi+qi是一定的,那么根据和积原理, p i + q i p_i + q_i pi+qi具有单调性,所以我们就可以使用二分查找枚举 p i p_i pi或 q i q_i qi来寻找答案啦!
在这里,我们枚举 p i p_i pi来找到答案,原因有两点:
-
p i p_i pi是较小值,只需枚举至 p i + q i 2 \frac{p_i + q_i}{2} 2pi+qi就可以得出答案了;
-
比起q
我更喜欢p
这个字母;
有不会和积原理的童鞋看这:
(反正我是都没看懂,比赛的时候直接用的二分,根本没考虑单调性,但是还是过了
代码:
Link#include <bits/stdc++.h>
#define int long long
using namespace std;
int k;
int n;
int e;
int d;
int h;
int x;
int sx;
int ef(int l, int r, int n, int m){
while(l<=r){
int mid=((l+r)>>1);
int j=mid*(m-mid);
if(j==n){
return mid;
}
else{
if(j<n){
l=mid+1;
}
else{
r=mid-1;
}
}
}
return -114514;
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>k;
while(k--){
cin>>n>>e>>d;
h=n-e*d+2;
sx=ceil(h/2);
x=ef(1ll,sx,n,h);
if(x!=-114514){
cout<<x<<" "<<h-x<<"\n";
}
else{
cout<<"NO\n";
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}