UVA10883 题解
FurippuWRY
·
·
题解
卡常大法好,1.78s 暴力模拟代码爆切蓝题!
题意就是有 n 个数,不断对相邻的两个数求平均值,输出最后剩下的那个数。
这不就纯模拟嘛,开个双重循环算就可以了,这还蓝啊?
卡常,打个 Ofast 和 cin 加速。
#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
typedef long long ll;
const ll N = 5e4 + 9;
ll t, n, num = 1;
double a[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> t;
while (t--) {
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
}
for (ll i = n - 1; i >= 1; i--) {
for (ll j = 1; j <= i; j++) {
a[j] = (a[j] + a[j + 1]) / 2.0;
}
}
printf("Case #%lld: %.3lf\n", num, a[1]);
++num;
}
return 0;
}
成功地用 1.78s 草过去了,遥遥领先。也许是暴力方法的最优解?
或许可以用快读快写,循环展开等方式卡到更少的用时,但我没试。
建议用 C++20 交,会更快一些。
给蓝题一点尊严,写个正解。
设数列为 \{a_n\},操作次数为 p,则有:
\begin{aligned}
p=0&:\{a_1,a_2,...a_n\}\\
p=1&:\{\dfrac{a_1+a_2}{2},\dfrac{a_2+a_3}{2},...,\dfrac{a_{n-1}+a_n}{2}\}\\
p=2&:\{\dfrac{a_1+2a_2+a_3}{4},\dfrac{a_2+2a_3+a_4}{4},...,\dfrac{a_{n-2}+2a_{n-1}+a_n}{4}\}\\
p=3&:\{\dfrac{a_1+3a_2+3a_3+a_4}{8},\dfrac{a_2+3a_3+3a_4+a_5}{8},...,\dfrac{a_{n-3}+3a_{n-2}+3a_{n-1}+a_n}{8}\}
\end{aligned}
手玩几个样例,可以推出最多能操作 n - 1 次。
不看分母,看每次操作完后每项的系数,可以发现这个系数规律和杨辉三角是一样的,那我们就可以推出:操作 n - 1 次后,剩下的数为
\displaystyle\sum^{n}_{i=1}\dfrac{\mathrm C_{n-1}^{i-1}a_i}{{2^{n-1}}}
设 $ans=\displaystyle\sum^{n}_{i=1}\dfrac{\mathrm C_{n-1}^{i-1}a_i}{{2^{n-1}}}$,则有
$$
\begin{aligned}
ans&=\displaystyle\sum^{n}_{i=1}\dfrac{\mathrm C_{n-1}^{i-1}a_i}{{2^{n-1}}}\\
&=\displaystyle\sum^{n}_{i=1}2^{\log_2\frac{\mathrm C_{n-1}^{i-1}a_i}{{2^{n-1}}}}\\
&=\displaystyle\sum^{n}_{i=1}2^{\log_2(\mathrm C_{n-1}^{i-1}a_i)-\log_22^{n-1}}\\
&=\displaystyle\sum^{n}_{i=1}2^{\log_2(\mathrm C_{n-1}^{i-1})+\log_2a_i-{n+1}}\\
\end{aligned}
$$
由于 $a_i$ 可以小于 $0$,那么
$$
\begin{aligned}
ans&=\displaystyle\sum^{n}_{i=1}2^{\log_2(\mathrm C_{n-1}^{i-1})+\log_2a_i-{n+1}}\\
&=\displaystyle\sum^{n}_{i=1}\mathrm{sgn} (a_i)\times 2^{\log_2(\mathrm C_{n-1}^{i-1})+\log_2|a_i|-{n+1}}\\
\end{aligned}
$$
其中
$$
\mathrm{sgn}(x)=
\begin{cases}
1,x>0\\
0,x=0\\
-1,x<0
\end{cases}.
$$
接着考虑解决 $\mathrm C_{n-1}^{i-1}$,因为
$$
\mathrm C_{n}^{m}=\dfrac{n!}{m!(n-m)!}
$$
则有
$$
\begin{aligned}
\mathrm C_{n}^{m+1}&=\dfrac{n!}{(m+1)![n-(m+1)]!}\\
&=\dfrac{n!}{(m+1)!(n-m-1)!}\\
&=\dfrac{n!}{m!\times(m+1)\times\dfrac{(n-m)!}{n-m}}\\
&=\dfrac{n!}{m!\times(n-m)!\times\dfrac{m+1}{n-m}}\\
&=\dfrac{n!}{m!\times(n-m)!\times\dfrac{m+1}{n-m}}\\
&=\dfrac{n!}{m!\times(n-m)!}\times\dfrac{n-m}{m+1}\\
&=\mathrm C_{n}^{m}\times\dfrac{n-m}{m+1}\\
\end{aligned}
$$
因为是从 $i=1$ 开始计算 $\mathrm C_{n-1}^{i-1}$ 的值,那么就可以用上面推导的公式来减少计算量。
```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t, n, num = 1;
double a;
int sgn(double x) {
if (x > 0) return 1;
if (!x) return 0;
if (x < 0) return -1;
}
int main() {
cin >> t;
while (t--) {
cin >> n;
double c = 0, ans = 0, p;
for (ll i = 1; i <= n; i++) {
cin >> a;
ans += sgn(a) * pow(2, c + log2(fabs(a)) - n + 1);
c += log2((n - i) * 1.0 / i);
}
printf("Case #%lld: %.3lf\n", num, ans);
++num;
}
return 0;
}
```