Loading

P10668 BZOJ2720 [Violet 5] 列队春游 (期望)

P10668 BZOJ2720 [Violet 5] 列队春游

期望

考虑每个元素什么情况下会产生贡献,然后分别贡献到答案中。当当前枚举的数 \(h_i\)\(i\)\(pre_i\) 之间有一个数字时那么会有对当前方案会有 \(1\) 的贡献。不妨将严格小于 \(h_i\) 的数的数量记为 \(s_i\),则大于等于 \(h_i\) 的数有 \(n-s_i\)

计算每个方案 \(h_i\) 产生的贡献。先看 \(i\)\(pre_i\) 之间的数产生给 \(h_i\) 的贡献,将大于等于 \(h_i\) 的数随机排列有 \((n-s_i)!\) 种。然后任意取一个小于 \(h_i\) 的数放到 \(i\)\(pre_i\) 之间都会产生同样的贡献 \(1\),有 \(s_i\) 种取法,剩下的数插进去随便排列,有 \(A_{n}^{s_i-1}\) 种,根据乘法原理,\(i\)\(pre_i\) 之间的数会产生贡献 \((n-s_i)!s_iA_{n}^{s_i-1}\),由于求的是期望,所以除以一个 \(n!\)

\[\frac{(n-s_i)!s_iA_{n}^{s_i-1}+n!}{n!}=\frac{(n-s_i)!s_iA_{n}^{s_i-1}}{n!}+1 \]

\(n!\) 的原因是不论有没有数对每个排列 \(h_i\)\(pre_i\) 都至少有 \(1\) 的贡献。再整理化简得到:

\[\frac{n+1}{n-s_i+1} \]

这是其中一个 \(h_i\) 的贡献,答案对于每个数都求一次累加即可。

就是从求一个方案的所有位置的贡献,到枚举每个 \(h_i\) 求贡献,到枚举 \(i\)\(pre_i\) 之间的其中一个数累加贡献。

复杂度 \(O(n)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1010;
int n;
int cnt[N];
double ans;
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n;
	int mx = 0;
	for (int i = 1; i <= n; i++) {
		int a;
		std::cin >> a;
		cnt[a]++;
		mx = std::max(mx, a);
	}

	int sum = 0;
	for (int i = 1; i <= mx; i++) {
		if(!cnt[i]) continue;
		ans += 1.0 * (n + 1) / (n - sum + 1) * cnt[i];
		sum += cnt[i];
	}
	std::cout << std::fixed << std::setprecision(2) << ans << "\n";

	return 0;
}
posted @ 2024-07-05 16:54  Fire_Raku  阅读(8)  评论(0编辑  收藏  举报