题解 UVA10428 【The Roots】

Hexarhy

2021-03-31 20:21:02

Solution

### Preface 牛顿迭代法模板题。但是被 UVA 这个屑 OJ 搞得体验非常不好/tuu。 **前置知识:导数入门。** ### Translation 给定一个 $n(n\le 5)$ 次多项式 $f(x)$,当 $n=0$ 时输入终止(样例没有但事实确实如此)。求出 $f(x)=0$ 所有根,升序输出。 保证存在 $n$ 个实根,且所有实根的绝对值 $\le 25$。有 $T(T\le 5000)$ 组询问。 ### Solution 我们来推一下牛顿迭代法。 假设已经得到了一个近似解 $x$,而答案为 $x_0$。这两点所在直线可以近似认为是过 $x_0$ 的切线。那么就得到: $$f'(x)=\frac{f(x)-f(x_0)}{x-x_0}$$ $f(x_0)=0$,并将上式化简得到: $$x_0=x-\frac{f(x)}{f'(x)}$$ 然后就可以递推做了。一般写法是根据 $f(x)$ 是否小于 $\epsilon$ 来继续迭代的。 > 牛顿迭代法的收敛率是平方级别的,这意味着每次迭代后近似解的精确数位会翻倍。 —— [OI Wiki](https://oi-wiki.org/math/newton/) 时间复杂度上通常认为是常数级别(就像并查集复杂度中的 $\alpha(n)$),并且 `<cmath>` 里的 `std::pow()` 是使用了牛顿迭代法的。 ----- 这题的第二步便是如何把 $n$ 个根都求出来。 刚开始很 sb 地想到用韦达定理折腾一下,结果发现直接把原多项式除以一个根的因式即可。 至于求除后的多项式,待定系数法手推一下也能出结果。这里直接给出结果: $$b_i=a_{i+1}+b_{i+1}\cdot x_0$$ $b_i$ 为除后的多项式系数,$x_0$ 是上一个求出的根。 ### Notice - 在本题如果写 $\epsilon$ 精度太高 TLE,精度太低 WA,可以考虑固定迭代次数,比如设置迭代 $t=150$ 次。 - 再次提醒 UVA 提交注意事项: - 行末不可以有多余空格。 - 结尾必须有且仅有一个空行。 - 题面虽然说精度要求 $10^{-10}$ ,但是必须要输出 $4$ 位小数。 ### Code ```cpp inline double f(cdb& x) { double res=0; for(int i=0;i<=n;i++) res+=a[i]*pow(x,i); return res; } inline double ff(cdb& x) { double res=0; for(int i=1;i<=n;i++) res+=i*a[i]*pow(x,i-1); return res; } inline void div(double* a,cdb& x) { double t[MAXN];memset(t,0,sizeof(t)); for(int i=n;i>=0;i--) t[i]=a[i+1]+t[i+1]*x; for(int i=0;i<=n;i++) a[i]=t[i]; } inline double solve(double x0) { for(int i=1;i<=150;i++) x0-=f(x0)/ff(x0); return x0; } int main() { for(int i=1,N=0;cin>>N;i++) { n=N;memset(a,0,sizeof(a)); for(int i=N;i>=0;i--) cin>>a[i]; if(N==0) continue; vector<double> ans; for(;n>=1;n--) { cdb x=solve(-30.0); ans.emplace_back(x); div(a,x); } sort(ans.begin(),ans.end()); printf("Equation %d: ",i); for(unsigned i=0;i<N-1;i++) printf("%.4f ",ans[i]); printf("%.4f\n",ans[N-1]); } return 0; } ```