1 条题解
-
0
自动搬运
来自洛谷,原作者为

周道_Althen
**搬运于
2025-08-24 22:05:55,当前版本为作者最后更新于2018-10-14 17:23:12,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
大家好我是出题人 Althen·Way·Satan(超小声)。热烈祝贺第一个切掉这个题的人:@南山桥一霸;
还有感谢一直到最后都在肝我的题的大佬,葱花都要哭出来了:@zhaimingshuzms。(Orz,都已经拿到70%了)
- 前排鸣谢:
指导(
PY关系):@PsyduckSPJ友情提供:@hdxrie
表情包出处:@全兽出击 画师:@呀嘛的小2狼 (
大家快去关注他呀XD)
一、题面解释
根据题面所描述的意思,我们可以得到如下的图形:


其中,绿色和蓝色的箭头代表走过的路程;红色的箭头代表走过的路程,设时间为的话,红色箭头的长度便为。

走路扭来扭去的,不方便研究,让人十分不爽。我们不妨 (
把他拖出去打一顿)平移一下他走过的路程:

平(
dǎlē)移(yídùn)过后,看起来舒服多了呢,那么现在,我们可以度量一下走过的路程,根据题面定义, 可以发现绿色箭头的长度为,绿色箭头的长度为。容易证明,不管怎么扭,都可以通过平移得到这样的表达式(小学数学)。
通过观察我们可以很容易发现,红色箭头的长度就刚刚好等于半径,所以我们不妨把他也旋转下来:

!?这是一个直角三角形,我们可以轻易写出下面的式子(
初中数学):化简过后可以得到:
所以说,我们定义函数:

求出函数在范围的根,就是我们要求的系数了。
注意,速度是矢量,所以就算我们解出来的根,带进原函数,,是负数,它也是合法的,这是高一的物理,题面最后也给了提示:$\color{#EEE}{\tt {Notice\ that\ SPEED\ is\ VECTOR.(High\ school\ physics)}}$。
二、分段解析
-
type1(10%):
答案只有可能为,直接判断该答案是否满足,既是否满足。

应该是读懂题意即可拿的分。
-
type2(20%):
因为保证,函数形式就保证为:,,。
的形式也可以保证为:
$$f(x)=(c_1^2-a_1^2-b_1^2)x^2+2(c_1c_0-a_1a_0-b_1b_0)x+c_0^2-a_0^2-b_0^2 $$
嗯……是一个二次函数呢,求根公式一波带走(
初中数学)。-
type3(30%):保证最多只有一个参数合法;
这相当于保证了在内最多只有一个零点,在内近似于单调。

很容易想到二分答案,也确实可以二分答案(
高一数学)。注意,如果二分的**check( )写的是的值,可能会造成比较大的精度误差,建议老老实实先算出,再check( )**判断的值。
在求的过程中,需要求到卷积,我们发现地求卷积代价太大,所以推荐使用 快速傅里叶变换 ,不会就出门左拐百度,会有很多大佬的讲解的。
-
type_all(100%)

没有什么特殊性质,因为可能有多个根,也不能二分了呢。

回到我们的问题,是如何求在内的任意一个解。
其实不过是个 牛顿迭代 的裸题,牛顿迭代常用于求一连续可导函数某一极值,或者某一零点。

这不就正是我们所需要的吗?
根据一阶牛顿迭代的公式,我们可以得到我们的答案就应该是以下函数的收敛值:
$$F(x)=F(x-1)-\frac{f\left(F(x-1)\right)}{f'\left(F(x-1)\right)} $$这里又需要求的一阶导,其实很简单,幂函数的导数网上一搜就有了。显然,我们后面的几个点,是满足二阶可导的,所以必然会收敛,但是不一定是在内,需要判断一下。
-
下面给出标程代码
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cctype> #include<cstdio> #include<string> #include<queue> #include<stack> #include<cmath> #include<map> #include<set> using namespace std; const double eps=1e-10; const double pi=acos(-1.0); inline int read(){ int x=0,f=1;char ch;ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=0;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch&15);ch=getchar();} if(f)return x;else return -x; } double fabs(double c){if(c<0.0)return -c;return c;} const int N=3e5+10; //FFT模板———————————————————————————————————— struct cpx{ double r,i; inline cpx operator *(const cpx&x)const{return (cpx){r*x.r-i*x.i,r*x.i+i*x.r};} inline cpx operator +(const cpx&x)const{return (cpx){r+x.r,i+x.i};} inline cpx operator -(const cpx&x)const{return (cpx){r-x.r,i-x.i};} }cpxa[N],cpxb[N]; int r[N]; void FFT(cpx*a,int f,int la){ int n=la; for(register int i=0;i<n;++i)if(i<r[i])swap(a[i],a[r[i]]); for(register int i=1;i<n;i<<=1){ cpx wn=(cpx){cos(pi/i),f*sin(pi/i)}; for(register int j=0;j<n;j+=(i<<1)){ cpx w=(cpx){1,0}; for(register int k=0;k<i;++k,w=w*wn){ cpx x=a[j+k],y=w*a[j+k+i]; a[j+k]=x+y;a[j+k+i]=x-y; } } } if(f==-1) for(register int i=0;i<n;i++)a[i].r/=n; } int merge_fft(cpx *a,cpx *b,int la,int lb){ int n=la,m=lb; int L=0;for(m+=n,n=1;n<=m;n<<=1)L++; for(register int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1)); FFT(a,1,n);FFT(b,1,n); for(register int i=0;i<=n;i++)a[i]=a[i]*b[i]; FFT(a,-1,n); return m; } //———————————————————————————————————————— double L,R; int La,Lb,Lc,Lc1,a[N],b[N],c[N],c1[N]; //函数平方 void get_square(int *a,int &L){ memset(cpxa,0,sizeof(cpxa)); memset(cpxb,0,sizeof(cpxb)); for(register int i=0;i<=L;i++) cpxb[i].r=cpxa[i].r=(double)a[i]; L=merge_fft(cpxa,cpxb,L,L); for(register int i=0;i<=L;i++)a[i]=(int)(cpxa[i].r+0.1); } //求C(x)的值 double get_val_C(double x){ double X=1,Ret=0; for(int i=0;i<=Lc;i++){Ret=Ret+c[i]*X;X=X*x;} return Ret; } //求C1(x)的值 double get_val_C1(double x){ double X=1,Ret=0; for(int i=0;i<=Lc1;i++){Ret=Ret+c1[i]*X;X=X*x;} return Ret; } //牛顿迭代 ——————————————————————————————————— int tim=30;//设定迭代次数tim double Newton_Iteration(double x){//输入F(x)的初值F(0),既迭代系数。 double c; while(1){ c=get_val_C(x);//求出f(F(x))的值 tim--; if(fabs(c)<eps)break;//若是F(x)在该精度下已经合法,便弹出。 x=x-c/get_val_C1(x);//求出F(x)-f(F(x))/f'(F(x))的值,赋给F(x+1) x=max(x,L);x=min(x,R);//防止越界 if(!tim)return 0;//若是在迭代次数tim内不合法,便弹出无解。 } return x; } int main() { La=read();Lb=read();Lc=read();scanf("%lf%lf",&L,&R); for(int i=0;i<=La;i++)a[i]=read(); for(int i=0;i<=Lb;i++)b[i]=read(); for(int i=0;i<=Lc;i++)c[i]=read(); for(register int i=0;i<=La;i++) cpxb[i].r=cpxa[i].r=(double)a[i]; //把c^2(x)-a^2(x)-b^2(x)存进c get_square(a,La); get_square(b,Lb); get_square(c,Lc); Lc=max(Lc,max(La,Lb)); for(register int i=0;i<=Lc;i++) c[i]=c[i]-b[i]-a[i]; //求c1(x)为c(x)的一阶导数 Lc1=Lc-1; for(int i=1;i<=Lc;i++) c1[i-1]=c[i]*i; double ans=Newton_Iteration((L+R)/2.0); if(tim) printf("%.8lf\n",ans); else printf("Inconsistent!\n"); return 0; }这道题就这样解完了。
题面清晰,解法自然,码量适中
什么?你不会牛顿迭代?那么我们马上进入下一个阶段。
三、重点考点总结——牛顿迭代
这里简单地讲一下** 一阶牛顿迭代 **,牛顿迭代是应用在最优化领域非常重要的一种算法,由于具有二阶收敛性,所以相比二分法能大大降低迭代次数,只能求一个可导函数的零点,或者有二阶导函数的极值,一种全局搜索算法用来解np问题最优解的算法,在算法竞赛中的运用比较少见(
Psyduck说)。先放wiki的动图,牛顿迭代动态示例图:

容易看出一个重要问题:对函数的一个点做切线,这个切线与轴的交点当做新的点,重复操作,得到的点就会越来越趋近于零点。
具体证明涉及 泰勒展开,就不细讲了(
其实是葱花Althen不会证明)。说到函数切线,自然就需要求导。
在上,点的斜率为,所以这个切线与轴的交点当做新的点,应该是。
所以,我们定义:
$$F(x)=F(x-1)-\frac{f\left(F(x-1)\right)}{f'\left(F(x-1)\right)} $$也就是不断去求点,可得这个点是越来越趋近某一个零点的。也就是说,我们的答案,就是,既函数的收敛值。
若二阶可导,那么在待求的零点值周围存在一个区域,只要起始点位于这个邻近区域内,那么牛顿迭代必定收敛。

不过……我们显然不需要算无限次,保证精度在一个范围内就行了,显然,牛顿迭代可以做到极快收敛到我们需要的精度,我们并不需要计算太多次。
还有!我们最终答案的计算效率、精度,还与迭代系数,也就是最初赋值的有很大关系。(
但是因为比较小的x取值范围,本题没有卡迭代系数的选定)。
然后贴出一阶牛顿迭代的模板:
(
如果迭代次数过少或者无解,那么会返回一个错误的答案)double Newton_Iteration(double F,int tim){//输入迭代系数F=F(0),迭代次数tim while(tim--)F=F-f(F)/f1(F);//f1(x)=f'(x) return F; }这里只是简单地讲一下** 一阶牛顿迭代 **,具体的讲解,有兴趣可以戳下面的链接,博主觉得讲得很清晰
(还有互交动画啊XD)。--·--·--《推荐讲解文章》--·--·--
如果对本题还有什么疑问或者更好的做法,欢迎洛谷私信我或者发送邮件到althenwaysatan@foxmail.com
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $

- 1
信息
- ID
- 3975
- 时间
- 1000ms
- 内存
- 63MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者