[数论] 小球碰撞


题目描述

[数论] 小球碰撞
[数论] 小球碰撞
[数论] 小球碰撞

输入

[数论] 小球碰撞

输出

[数论] 小球碰撞

样例输入

2 3 1

样例输出

2.5


解题思路

为了方便,我们把mama记作aa,把mbmb记作bb,把PP记作cc,那么题目就变成了:
已知a,b,ca,b,c,求ax+by=cax+by=c的一组解,使得0.5ax2+0.5by20.5*a*x^2+0.5*b*y^2最小,输出这个最小值
显然,第一步我们用拓展欧几里得算法求出这个不定方程的一组特解,为{x0y0\begin{cases}x_0 \\ y_0 \\ \end{cases},令aabb的最大公因数为dd,那么通解为{x1=x0+bdty1=y0adt\begin{cases}x_1=x_0+\frac{b}{d}t \\ y_1=y_0-\frac{a}{d}t\\ \end{cases}
所以ans=0.5(ax12+by12)ans=0.5(ax_1^2+by_1^2)x1=x0+bdtx_1=x_0+\frac{b}{d}ty1=y0adty_1=y_0-\frac{a}{d}t代入这个式子化简后可得:ans=0.5(a+bd2abt2+2ab(x0y0)dt+ax02+by02)ans=0.5(\frac{a+b}{d^2}abt^2+2\frac{ab(x_0-y_0)}{d}t+ax_0^2+by_0^2)
这就是一个关于tt的二次函数,对称轴为:t1=ab(x0y0)da+bd2ab=(y0x0)da+bt_1=-\frac{\frac{ab(x_0-y_0)}{d}}{\frac{a+b}{d^2}ab}=\frac{(y_0-x_0)d}{a+b}
再在t1t_1两边的两个整点(也就是t1\lfloor t1 \rfloort1+1\lfloor t1 \rfloor+1)中去找答案即可


参考代码

#include <cstdio>
#include <cstring>
#include <cmath>
#define reg register
 
template <class T>
inline T read() {
    T x = 0; T f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - 48; s = getchar();}
    return x * f;
}
 
template <typename T>
inline void wri(T x) {
    if(x < 0) {x = -x; putchar('-');}
    if(x / 10) wri(x / 10);
    putchar(x % 10 + 48);
}
 
template <typename T>
inline void write(T x, char s) {
    wri(x);
    putchar(s);
}
 
template <typename T>
inline T Min(T x, T y) {return x < y ? x : y;}
 
#define LL long long
 
int ma, mb, p, gcd;
double va, vb, t, tmp, pa, pb, ans;
 
inline int exgcd(int a, int b, double &x, double &y) {	//拓展欧几里得算法
    if(! b) {
        x = 1, y = 0;
        return a;
    }
    else {
        int tmp = exgcd(b, a % b, y, x);
        y -= x * (a / b);
        return tmp;
    }
}
 
int main() {
    ma = read<int>(), mb = read<int>(), p = read<int>();
    gcd = exgcd(ma, mb, va, vb);
    va *= (p * 1.0 / gcd), vb *= (p * 1.0 / gcd);	//算出特解
    if(p % gcd != 0) {	//判断无解
        puts("-1");
        return 0;
    }
    t = (vb - va) * gcd * 1.0 / (ma + mb);	//算出t1并在两边找答案
    tmp = floor(t);
    pa = va + tmp * (mb / gcd);
    pb = vb - tmp * (ma / gcd);
    ans = 0.5 * ma * pa * pa + 0.5 * mb * pb * pb;
    tmp = floor(t) + 1;
    pa = va + tmp * (mb / gcd);
    pb = vb - tmp * (ma / gcd);
    ans = Min(ans, 0.5 * ma * pa * pa + 0.5 * mb * pb * pb);
    LL q = ans;	//判断是否为浮点数
    if(ans != q)
        printf("%.1lf\n", ans);
    else
        write(q, '\n');
    return 0;
}