P5139 z小f的函数 题解

· · 题解

AK 竞赛后发题解,RP++!

这是一道很好的小学数学题,能教会我们二次函数移动问题。

我花了半小时,A 了这道题,给大家讲讲做法。

对五个指令,我们进行分开处理:

指令 1、2:考察的是二次函数的移动法则:

左加右减,上加下减。

意思是,对一个二次函数(顶点式)

f(x)=a\cdot (x+k)^2+c

向左(右)移 k 会增加(减少);

向上(下)移 c 会增加(减少)。

于是上移代码如下:

scanf("%lf",&k1);c+=k1;

右移有些复杂,步骤如下:

  1. 将函数转为顶点式

  2. 在顶点式进行右移操作

  3. 顶点式转为一般式

代码如下:

inline void rigt(ld a,ld &b,ld &c,ld w){
    ld d=b/(2*a)-w,e=(4*a*c-b*b)/(4*a);
    c=e+d*d*a;b=d*2*a;return;
}

指令 3(有些麻烦,毕竟有 30% 数据没有指令 3)

这考察二次函数对称轴的转换和找特殊点。

对一个二次函数

f(x)=a\cdot x^2+b\cdot x+c

一定有且仅有一个对称轴:

x=-\frac{b}{2a}

当函数图像关于点 P(x_1,y_1) 进行对称变换时,有以下 3 个结论:

  1. 开口方向会变;

  2. 对称轴会对直线 x=x_1 进行对称变换;

  3. 函数中所有点都会关于点 P(x_1,y_1) 进行对称变换。

所以,指令 3 步骤如下:

  1. 计算原对称轴;

  2. 计算新对称轴;

  3. 用新对称轴倒推得到 y

  4. 因为当 x=0 时,f(x)=c,所以原函数过点 Q(0,c)Q 点关于 P 点进行对称变换,得到

Q'(2\cdot x_1,2\cdot x_2-c)

由结论 3 可知,Q' 在新函数图像上。

代码如下:

inline void CHS(ld &a,ld &b,ld &c,ld x1,ld y1){
    ld zhou=-b/(2*a);a=-a;
    b=-(2*a)*(x1*2-zhou);
    ld x2=2*x1,y2=y1*2-c;
    c=y2-a*x2*x2-b*x2;return;
}

指令 4:求函数区间最值。

前置知识:对于二次函数

f(x)=a\cdot x^2+b\cdot x+c

他在闭区间 [k_1,k_2] 的最值只会出现在 k_1k_2 或顶点上。

所以,先判断顶点是否在闭区间 [k_1,k_2] 上,再求最值。

代码如下:

inline ld Min(ld a,ld b,ld c,ld k1,ld k2){
    if(k1>k2)swap(k1,k2);
    ld dl=k1*k1*a+k1*b+c,dr=k2*k2*a+k2*b+c;
    ld dm=-b/(2*a),tans=min(dl,dr);
    if(dm>=k1&&dm<=k2)
        tans=min(tans,dm*dm*a+dm*b+c);
    return tans;
}
inline ld Max(ld a,ld b,ld c,ld k1,ld k2){
    if(k1>k2)swap(k1,k2);
    ld dl=k1*k1*a+k1*b+c,dr=k2*k2*a+k2*b+c;
    ld dm=-b/(2*a),tans=max(dl,dr);
    if(dm>=k1&&dm<=k2)
        tans=max(tans,dm*dm*a+dm*b+c);
    return tans;
}

指令 5:这是除指令 1 之外最简单的了。

题意即 a\cdot x^2+b\cdot x+c=u\cdot x^2+v\cdot x+w

用左式减去右式,得到

(a-u)\cdot x^2+(b-v)\cdot x+(c-w)=0

注意到 a\ne u,问题转为求一元二次方程是否有解。

即判别式 (b-v)^2-4(a-u)\cdot(c-w) 是否非负。

代码如下:

inline int EJ(ld a,ld b,ld c,ld u,ld v,ld w){
    a-=u;b-=v;c-=w;
    if(b*b>=4*a*c)return 2;
    else return 0;
}

综上,无注释 AC 代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef double ld;
inline void rigt(ld a,ld &b,ld &c,ld w){
    ld d=b/(2*a)-w,e=(4*a*c-b*b)/(4*a);
    c=e+d*d*a;b=d*2*a;return;
}
inline void CHS(ld &a,ld &b,ld &c,ld x1,ld y1){
    ld zhou=-b/(2*a);
    b=-(2*(a=-a))*(x1*2-zhou);
    ld x2=2*x1,y2=y1*2-c;
    c=y2-a*x2*x2-b*x2;return;
}
inline ld Min(ld a,ld b,ld c,ld k1,ld k2){
    if(k1>k2)swap(k1,k2);
    ld dl=k1*k1*a+k1*b+c,dr=k2*k2*a+k2*b+c;
    ld dm=-b/(2*a),tans=min(dl,dr);
    if(dm>=k1&&dm<=k2)
        tans=min(tans,dm*dm*a+dm*b+c);
    return tans;
}
inline ld Max(ld a,ld b,ld c,ld k1,ld k2){
    if(k1>k2)swap(k1,k2);
    ld dl=k1*k1*a+k1*b+c,dr=k2*k2*a+k2*b+c;
    ld dm=-b/(2*a),tans=max(dl,dr);
    if(dm>=k1&&dm<=k2)
        tans=max(tans,dm*dm*a+dm*b+c);
    return tans;
}
inline int EJ(ld a,ld b,ld c,ld u,ld v,ld w){
    a-=u;b-=v;c-=w;
    if(b*b>=4*a*c)return 2;
    else return 0;
}
inline void gofans(){
    ld a,b,c,k1,k2,u,v,w;
    int n,p;scanf("%lf%lf%lf%d",&a,&b,&c,&n);
    while(n--){
        scanf("%d",&p);
        switch(p){
            case 1:scanf("%lf",&k1);c+=k1;break;
            case 2:scanf("%lf",&k1);rigt(a,b,c,k1);break;
            case 3:scanf("%lf%lf",&k1,&k2);
                    CHS(a,b,c,k1,k2);break;
            case 4:scanf("%lf%lf",&k1,&k2);
            printf("%.2lf %.2lf\n",Min(a,b,c,k1,k2),Max(a,b,c,k1,k2));
            break;case 5:scanf("%lf%lf%lf",&u,&v,&w);
            printf("%d\n",EJ(a,b,c,u,v,w));break;
            default:while(true)printf("NO-ANSWER!\n");break;
        }
    }
    k1=-b/(2*a);
    printf("%.2lf\n",k1*k1*a+k1*b+c);
    return;
}
int main(){
    int T;scanf("%d",&T);
    while(T--)gofans();
    return 0;
}

完结撒花!