这题居然是我为数不多的几道在考场上想出正解的数论题(虽然没A),真是太感动了。考完后问了下同学的方法,暴力枚举(从-10000枚举都10000)居然能得到一半的分,也是很神奇了。
这给了我们一个启示,不能保证100%做对的题都要打包搜。
先放出题目惊吓一下你们幼小的心灵。(不是我吹,能一遍理解的都是大佬)
题目描述
输入
输出
样例输入
样例1输入: 2 3 1 样例2输入: 232 320 34272
样例输出
样例1输出: 2.5 样例2输出: 1102656
提示
好了让我来用人话解释一下:给出两个值a,b,和c,求其中方程(ax+by=c)的解x,y代入E(x)+E(y)的最小值.。 。其中。
那么也就是说是求一个方程的解经过一定计算后的最小值。
在这里科普一下,解二元一次方程我们一般都是用扩展欧几里得算法。我们都知道辗转相除法是求两个数的最大公因数,那么魔改扩展欧几里得算法在这基础上扩展了一下,他不仅可以就出最大公因数,还可以求出如下方程的一个解,也就是特解。
(2式)。
假装我们都知道(1式)这个方程有整数的解的情况下必须满足(c能被x,y的最大公因数整除)。这里证一下。
将1式的x,y替换一下可得(设gcd(x,y)=d:
合并同类项:
因为a,k1,b,k2都是整数,所以
那么我们就可以在二式左右两边乘以一个c/d(假定我们已经用扩展欧几里得算法求出了x1,y1)。那么1式就可以转换为:
.
现在给出扩展欧几里得算法(具体证明请百度)
void exgcd(LL a, LL b,LL &d,LL& x, LL& y)//d为gcd(x,y),x,y为一组特解(公式里的x1,y1)
{
if(!b)
x = 1,y = 0,d=a;
else {
exgcd(b, a%b, d ,y, x);
y -= x*(a/b);
}
}
现在我们已经求出了一组特解。但我们并不知道这组解计算后是否最小,所以我们还要求出通解。
下面给出由特解求出通解的方法:
(其中t为任意整数,u=c/d,v=-b/d)
现在知识储备已经完善,现在让我们来思考一下如何求出最小解(当然不是用暴力枚举)
回忆一下当时考试的情景,我正在冥思苦想之时,看到了数学知识补充,嗯?
里面有一句,当时,y最小,那么这就是此题的突破口了.
因为我们要求的最小值是E(x)+E(y),那么不妨将E(x)+E(y)看成y,再求他的最小值。
所以可以得出式子:(3式),又有,化一下可得,代入到3式之后化简,就可以求出二次函数中的系数a,b,便可以算出最小的x.
这里不给出化简过程,化简后可得。
光算出rx还不够,我们还要算出ry,这里直接通过这个式子求出,便可以算出ry来.
对了,还有,因为rx不一定是整数,而t是整数。我的解决方案是求t取t,t-1,t+1算出来之后的最小值。
#include
#include
using namespace std;
#define LL long long
#define N 100010
#define mem(a,n) memset(a,n,sizeof(a))
int read() {
int f=1,s=0;char a=getchar();
while(a<'0' || a>'9') { if(a=='-') f=-1; a=getchar(); }
while(a>='0' && a<='9') { s=s*10+a-'0'; a=getchar(); }
return f*s;
}
LL m1=read(),m2=read(),P=read(),va,vb,d,u,v,T;
double rva;
void exgcd(LL a, LL b,LL &d,LL& x, LL& y)
{
if(!b)
x = 1,y = 0,d=a;
else {
exgcd(b, a%b, d ,y, x);
y -= x*(a/b);
}
}
double E(double v,double m) {
return m*v*v/2;
}
int main() {
exgcd(m1,m2,d,va,vb);
if(P%d!=0) {
cout<<"-1";
return 0;
}
va*=P/d;
vb*=P/d;
u=m2/d,v=-m1/d;
rva=P*1.0/((m1+m2)*1.0);
T=(rva-va*1.0)/u;
double ans=min( E(va+u*T,m1)+E(vb+v*T,m2),min(E(va+u*(T+1),m1)+E(vb+v*(T+1),m2),E(va+u*(T-1),m1)+E(vb+v*(T-1),m2)));
LL ans1=ans;
if(ans1==ans)
cout<