题目描述

ns 在园子里过着快乐而充实的生活。突然有一天,从天而降 $n$ 根柱子,这 $n$ 根柱子在学堂路上排成一排,第 $i$ 根柱子高度为 $h_i$ (即 $h_i$ 为原始高度) 米,然而,当有人站在第 $i$ 根柱子上的时候,第 $i$ 根柱子的高度会下降 $t_i$ (即当有人在上面时候 $h_i - t_i$ 就是当前高度) 米,离开之后又会恢复。

ns 对这些柱子非常感兴趣,他可以从一根柱子跳到任意一个 (包括他原本站着的柱子) 原始高度不超过他所在柱子当前高度的柱子上,如果有多个柱子都满足,他会随机等概率地跳到一个满足条件的柱子上,如果没有柱子满足,他就会跳到地上。

ns 想知道,如果他从第 $i$ 个柱子开始,期望跳多少次可以跳到地上。

输入格式

第一行包含一个正整数 $n$ ($n \leq 10^6$),表示柱子的数量。

第二行包含 $n$ 个空格隔开的正整数 $h_i$ ($h_i \leq 10^5$),表示每根柱子的高度 (单位:米)。

第三行包含 $n$ 个空格隔开的非负整数 $t_i$ ($0 \leq t_i \leq h_i$),表示有人站在第 $i$ 根柱子上的时候高度会下降 $t_i$ 米。

输出格式

输出一行 $n$ 个浮点数,每两个浮点数用一个空格隔开,第 $i$ 个数表示 ns 从第 $i$ 根柱子开始,期望跳多少步可以跳到地上,如果期望步数为无穷,输出 $0$。

如果你输出的每个浮点数与标准答案的误差 (此处误差定义为你的答案与标准答案差的绝对值) 均不超过 $0.001$ ,你的答案将被视为正确。

题解

注意到跳柱子形成的关系像一个 DAG,因此尝试使用期望 DP 来完成。

设 $f_i$ 表示从柱子 $i$ 开始跳回地面的期望步数,再记 $h'_i = h_i - t_i$ 为有人在上面时的高度,则如果 $h'_i < h_i$ 时就有

$$ f_i = 1 + \dfrac {\displaystyle \sum_{h_j \leq h'_i} f_j} {\displaystyle \sum_{h_j \leq h'_i} 1} $$

当然,如果不存在 $h_j \leq h'_i$ 的 $j$,则 $f_i = 1$ (不存在满足条件的柱子,就会跳到地上)。

但是,对于 $h'_i = h_i$ 的柱子,就有可能自己跳到自己上面,从而造成循环转移。那该怎么处理呢?

正确的思路是,对所有的 $h'_i = h_i$ 且高度相同的柱子,一起处理。设一共有 $k$ 个这样的柱子,设期望步数为 $f$,则

$$ f = 1 + \dfrac {\displaystyle \sum_{\substack {h_j \leq h_i \\ h'_j < h_i}} f_j + k \cdot f} {\displaystyle \sum_{h_j \leq h_i} 1} $$

设 $A = \displaystyle \sum_{\substack {h_j \leq h_i \\ h'_j < h_i}} f_j$,$B = \displaystyle \sum_{h_j \leq h_i} 1$。则有方程 $f = 1 + \dfrac {A + k \cdot f} B$,解之,得 $f = \dfrac {A + B} {B - k}$。

当然,如果 $B = k$,则此时所有高度不超过 $h_i$ 的柱子的高度均为 $h_i$ 且 $h'_i = h_i$。因此这种情况永远跳不到地上,故此时 $f = + \infty$。

最后讲一下枚举顺序,可以发现,我们应该按照 $h_i$ 从小到大进行枚举,先处理 $h'_i < h_i$ 的,最后统一处理 $h'_i = h_i$ 的。记得在处理的过程中维护 $\sum_j f_j$ 和高度不超过 $h_i$ 的柱子个数。

总时间复杂度 $O \left( n + h_i \right)$ (计数排序) 或 $O \left( n \log n \right)$ (其它排序)。

代码

#include <bits/stdc++.h>
#define N 1000005
#define M 100005
#define x first
#define y second
#define ID isdigit(c = *next++)

struct Istream {
	char *next, buf[20030731];
	void init(FILE *f = stdin) {fread(buf, 1, sizeof buf, f); next = buf;}
	Istream & operator >> (int &x) {
		int c; x = 0;
		for (; !ID; ) if (!~c) return *this;
		for (x = c & 15; ID; x = x * 10 + (c & 15)) if (!~c) break;
		return *this;
	}
} cin;

typedef std::pair <int, int> pr;
typedef std::vector <pr> vec;

int n;
int H[N], h[N], sc[M];
double f[N], sf[M];
vec A[M];

int main() {
	int i, j; double r;
	cin.init(); cin >> n;
	for (i = 1; i <= n; ++i) cin >> H[i];
	for (i = 1; i <= n; ++i) {
		cin >> j; h[i] = H[i] - j;
		A[H[i]].emplace_back(h[i], i);
	}
	for (i = 1; i <= 100000; ++i) {
		sc[i] = sc[i - 1] + (int)A[i].size();
		sf[i] = sf[i - 1]; j = 0;
		for (pr t : A[i])
			t.x < i ? (void)(sf[i] += f[t.y] = (sc[t.x] ? sf[t.x] / (double)sc[t.x] : 0.0) + 1.0, sf[i]) : (void)(++j);
		r = (j == sc[i] ? INFINITY : (sf[i] + (double)sc[i]) / (double)(sc[i] - j));
		for (pr t : A[i])
			if (t.x == i) sf[i] += f[t.y] = r;
	}
	for (i = 1; i <= n; ++i)
		printf("%.8lg%c", std::isfinite(f[i]) ? f[i] : 0.0, i == n ? 10 : 32);
	return 0;
}

坑1:注意变量 $A$ 维护的是对于 $h_j \leq h_i; h'_j < h_i$ 的 $f_j$ 求和,故最好在处理 $h'_i < h_i$ 的柱子时计算好,不要漏算或多算了。