2019.7.9 校内测试题 平方根
题目
平方根(sqrt.cpp,1s,128MB)
【问题描述】:
给出一个正整数 n(1<n<=2^31-1),求当 x,y 都为正整数,方程: sqrt(n)=sqrt(x)-sqrt(y) 的解中,x 的最小值是多少?
【输入文件】:
输入文件 sqrt.in 只有一行,一个正整数 n。
【输出文件】:
输出文件 sqrt.out 只有一行,即满足条件的最小的 x 的解。
【输入输出样例】:
sqrt.in 4
sqrt.out 9
【数据规模】:
30%的数据满足 1<n<=10000; 100%的数据满足 1<n<=2^31-1。
考试得分: 30
主要算法 : 质数(质因数分解)
应试策略:
- 看到根式sqrt(n)=sqrt(x)-sqrt(y),又因为三者都是正整数,所以x>n,数学老师经常说遇到根式就平方,但是怎样平方呢?需要分离变量吗?
- 首先将等式进行变换,分离变量y=x+n-2*sqrt(x*n),又因为y为整数,所以sqrt(x*n)也为整数
- n是已知量,从小到大枚举x就可以算出sqrt(x*n)是否是整数就行
- 符合条件就过河拆桥式输出
代码
#include<math.h> #include<stdio.h> #include<stdlib.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) #define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,10000,stdin),pa==pb)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin),freopen(name".out","w",stdout) using namespace std; static char buf[10000],*pa=buf,*pb=buf; inline int read(); int n,t; long double p1; long long p2; int main() { File("sqrt"); n=read(); while(++t) { p1=n+t+2*sqrt(t*n),p2=p1; if(p1==p2) {printf("%lld",p2);return 0;} } printf("NO WAY!"); return 0; } inline int read() { register int x(0);register int f(1);register char c(gc); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; }
非完美算法:
照搬应试策略
正解:
- 沿袭应试策略的1,2点的基础上,在判断sqrt(x*n)上进行优化
- 已知x*n必须是完全平方数,那么将其乘积数进行质因数分解,那么质因数的幂模2都为0
- 所以将已知量n进行质因数分解,把质因数的幂模2不是0的数筛选出来,将x变为筛选出数的乘积,得到一个暂时符合条件下的替补x
- 又因为x>n,所以在x的基础上再乘上以个平方数,直到乘积大于n就输出
代码
#include<math.h> #include<stdio.h> #include<stdlib.h> #define LL long long #define FORa(i,s,e) for(LL i=s;i<=e;i++) #define FORs(i,s,e) for(LL i=s;i>=e;i--) #define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,10000,stdin),pa==pb)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin),freopen(name".out","w",stdout) using namespace std; static char buf[10000],*pa=buf,*pb=buf; inline LL read(); LL n,t,cnt,x=1; long double p1; long long p2; struct Node{ LL p,k; }a[32]; int main() { File("sqrt"); n=read(); LL p=n; FORa(i,2,sqrt(p)) { LL ct=0; if(p%i==0) { ++cnt,a[cnt].p=i; while(p%i==0) ct++,p/=i; a[cnt].k=ct; } } if(p>1) a[++cnt].p=p,a[cnt].k=1; FORa(i,1,cnt) if(a[i].k%2==1) x*=a[i].p; LL t=2; while(x*t*t<=n) t++; printf("%lld",x*t*t); return 0; } inline LL read() { register LL x(0);register LL f(1);register char c(gc); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; }
总结:
- 现将数学式子化为“最简式”,在根据数学基本知识思考
- 暴力打起来就是要快而准,但是正解至少也要思考
- 枚举将位置变成已知,尽可能消元