「一本通 1.2 练习 3」灯泡(三分/公式法)(三角函数,计算几何)

传送门

这道题要用带一点点三角函数。。。

不用怕,只要有理性的思维,是可以知道怎么做的

度娘!

说说我对三角函数的理解吧,简单来说,就是如果你知道直角三角形的一个锐角,那你就知道了这个直角三角型的形状了(求出三个角的角度数),那么如果由另一个直角三角型的三个角也跟这个三角形相等,那么他们两个是可以通过比例转化的,他们三条边中任意两条边之比也相等(也就是任意两条边之比如果角的度数固定了,那么这俩条边的比就固定了)。

安利:

那么,假设人的影子没有在墙上,那么,人从灯底往右走,走越远影子越长!那么最长的情况就是这样:

由于比相同,设影长为L,设人离灯x米远。

\[H:D=h:L\]

\[所以Dh=HL\]

\[Dh/H=L\]

那么我们得出了,在影子全在地上,L的最大值为Dh/H,这时x为D-Dh/H,设left=D-Dh/H

那么,如果影子全在墙上,则L=h,x=D,设right为D。

如果影子在墙上,就比较抽象了。。。

然后呢。。。?

那么:

\[那么(H-h):x=kk:(D-x)\]

\[(H-h)*(D-x)=x*kk\]

\[(H-h)*(D-x)/x=kk\]

那么在墙上的影子就是

\[h-kk=h-(H-h)*(D-x)/x=H-(H-h)*D/x\]

\[L=D-x+(h-kk)=D-x+H-(H-h)*D/x\]

\[=D+H-(x+(H-h)*D/x)\]

那么就是要让\(x+(H-h)*D/x\)取最小值,可以证明,在正数区间,\(x+(H-h)*D/x\)是个开口向上的单峰函数。

证:

情况1:x增加y,\((H-h)*D/x\)减小的数大于y

情况2:x增加y,\((H-h)*D/x\)减小的数小于y

又由于x越大,\((H-h)*D\)/x减小的数字越小,所以,会由情况1慢慢转为情况2,于是便由下降变为上升,成单峰势。

于是,\(D+H-(x+(H-h)*D/x)\)便是个开口向下的单峰函数!(那你整这么一大坨有什么用?)

早说有图片!

实现:

l=left,r=right;

当答案=l时,代表影子全在地上的最大值。

当答案=r是时,代表影子全在墙上的最大值

当答案=(l,r)时,代表影子一半在墙上,一半在地上的最大值。

代码:

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  ll;
inline  double  mymax(double  x,double  y){return  x>y?x:y;}
inline  double  cai(double  H,double  h,double  D,ll  x)
{
    double  xx=x/10000.0;
    return  H+D-xx-((H-h)*D)/xx;
}//之前推出的函数
double  sanfen(double  H,double  h,double  D)
{
    ll  l=ll((D-(h*D)/H)*10000.0),r=ll(D*10000.0);//乘以10000转ll
    ll  m1,m2;
    while(l<r)//三分
    {
        m1=(l+r)/2;m2=(l+r)/2+1;
        if(cai(H,h,D,m1)>cai(H,h,D,m2))r=m1;
        else  l=m2;
    }
    return  cai(H,h,D,l);//真象只有一个,l或r就是答案
}
int  main()
{
    int  T;scanf("%d",&T);
    while(T--)
    {
        double  H,h,D;scanf("%lf%lf%lf",&H,&h,&D);
        printf("%.3lf\n",sanfen(H,h,D));//输出
    }
    return  0;
}

公式法:

因为这个我看了好久题解(泪奔)(:光速逃

那么,如果是经验丰富的巨佬,一定会想到公式法。。。

\[我们设(H-h)*D为A,设B、C、D为x的一个取值。\]

\[C为x+(H-h)*D/x的单峰\]

\[且B<C<D\]

\[则有B+A/B>C+A/C<D+A/D\]

\[由B+A/B>C+A/C所得\]

\[B-C>A/C-A/B\]

\[B-C>A(B-C)/BC\]

\[BC(B-C)>A(B-C)\]

\[BC<A\]

\[又因为C<B,所以BC<C^2\]

那是不是代表\(A=C^2\)证出来了,耶!想太多

安利:

\[但是,由C+A/C<D+A/D可得\]

\[C-D<A/D-A/C\]

\[C-D<A(C-D)/CD\]

\[CD(C-D)<A(C-D)\]

\[CD>A\]

\[又因为D>C,所以CD>C^2\]

那么我们就可以名正言顺说\(A=C^2\)耶!

所以\(C=sqrt(A)\)

所以,当\(x\)等于\(sqrt((H-h)*D)\)时,\(x+(H-h)*D/x\)位于单峰上,同时\(D+H-(x+(H-h)*D/x)\)也位于单峰上

当然,当\[x<=left\]时,由于\(D+H-(x+(H-h)*D/x)\)是个开口向下的单峰函数,且\(x=[left,right]\)所以x=left。

同理当\(x>=right\)时,x=right!

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
using  namespace  std;
int  main()
{
    int  T;scanf("%d",&T);
    while(T--)
    {
        double  H,h,D;scanf("%lf%lf%lf",&H,&h,&D);
        double  x=sqrt((H-h)*D);
        if(x<=D-(h*D)/H)printf("%.3lf\n",(h*D)/H);//全在地上
        else  if(x>=D)printf("%.3lf\n",h);//全在墙上
        else  printf("%.3lf\n",H+D-x*2);//一部分在地上,一部分在墙上
    }
    return  0;
}

光速逃,耶!写完了!

原文地址:https://www.cnblogs.com/zhangjianjunab/p/9855482.html

时间: 2024-08-28 19:43:08

「一本通 1.2 练习 3」灯泡(三分/公式法)(三角函数,计算几何)的相关文章

10249「一本通 1.3 例 5」weight

#10249「一本通 1.3 例 5」weight 题目描述 原题来自:USACO 已知原数列a1,a2,...,an中的前1项,前2项,前3项,... ,前 n 项的和,以及后 1 项,后 2 项,后 3 项,...,后 n 项的和,但是==所有的数都被打乱了顺序==.此外,我们还知道数列中的数存在于集合 S 中.试求原数列.当存在多组可能的数列时,求字典序最小的数列. 输入格式 第 1 行,一个整数 n . 第 2 行, 2 × n 个整数,注意:数据已被打乱. 第 3 行,一个整数 m ,

「一本通 4.1 练习 2」简单题

题目描述 题目来源:CQOI 2006 有一个 n 个元素的数组,每个元素初始均为 0.有 m 条指令,要么让其中一段连续序列数字反转--0 变 1,1变 0(操作 1),要么询问某个元素的值(操作 2). 例如当 n=20 时,10 条指令如下: 操作 回答 操作后的数组 1 1 10 N/A 11111111110000000000 2 6 1 11111111110000000000 2 12 0 11111111110000000000 1 5 12 N/A 11110000001100

「一本通 4.2 例 2」最敏捷的机器人(loj10120)

题目描述 Wind 设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了-- 机器人们都想知道谁是最敏捷的,于是它们进行了如下一个比赛.首先,他们面前会有一排共 n 个数,它们比赛看谁能最先把每连续 k 个数中最大和最小值写下来,当然,这些机器人运算速度都很快,它们比赛的是谁写得快. 但是 Wind 也想知道答案,你能帮助他吗? 输入格式 第一行为 n,k,意义如题目描述. 第二行共 n 个数,为数字序列,所有数字均在 Pascal 的 longint 范围内,即所有数均为整数,且

loj10139. 「一本通 4.5 练习 1」树上操作(loj2125. 「HAOI2015」树上操作 )

思路: 第二遍dfs时记录end[x]为在结点序列中以x为根的子树最后访问的节点,写个线段树标记下传即可.与值有关的数据注意long long #include<cstdio> #include<iostream> #include<vector> #include<cmath> #include<string> using namespace std; const int maxn = 100010; inline void qread(int

loj10143. 「一本通 4.6 例 1」营业额统计

思路: 使用treap在线存数据,每次取出新读入数据的前驱与后继,与该数据差值较小的就是.(注意若已有同一个数据,特判ans+=0) #include<cstdio> #include<iostream> #include<cstdlib> #include<cmath> using namespace std; const int maxn = 100010; inline void qread(int &x) { x = 0; register

loj10144. 「一本通 4.6 练习 1」宠物收养所

思路: treap+简单模拟. 我只能说:注意取模!!!! #include<cstdio> #include<iostream> #include<cstdlib> #include<cmath> using namespace std; const int maxn = 80010; inline void qread(int &x) { x = 0; register int ch = getchar(), flag = 0; while(ch

loj10145. 「一本通 4.6 练习 2」郁闷的出纳员

思路: 简单的treap+模拟. 但这道题的题面有误导群众的嫌疑,因为题中并没有明确刚进入公司就离开的算不算在离开公司的人中. 当然,对于这道题的数据,题目意思是不算在内. #include<cstdio> #include<iostream> #include<cstdlib> #include<string> using namespace std; const int maxn = 80010; inline void qread(int &x

loj10141. 「一本通 4.5 练习 3」染色

思路: 利用树链剖分转化为线段树问题,考虑线段上经这两种操作后的sum求值方法,并着重考虑树链合并的情况即可.线段树为了打cov,要注意将所有的颜色统一加一. #include<cstdio> #include<iostream> using namespace std; const int maxn = 100010; inline void qread(int &x){ x = 0; register int ch = getchar(); while(ch <

loj10147. 「一本通 5.1 例 1」石子合并

思路: 经典的区间dp,题解到处都是... #include<cstdio> #include<iostream> #include<cstdio> using namespace std; const int maxn = 1010; inline void qread(int &x){ x = 0; register int ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); w