Live2D

Solution -「CF 494C」Helping People

Description

  Link.

  给定序列 {an}m 个操作,第 i 个操作有 pi 的概率将 [li,ri] 内的元素 +1。且保证任意两个区间要么不交,要么有包含关系。求所有操作完成后序列最大值的期望。

  n105m5000

Solution

  首先应当知道,E(max{ai})max{E(ai)}(不然还需要做嘛 qwq),这是由于每个数的期望值是不独立的。

  从题目奇怪的限制入手——各区间构成树形关系,整个序列上的区间构成一片森林。不妨加入第 m+1 个操作区间,满足 lm+1=1,rm+1=n,pm+1=0,区间就构成一棵严格的树了。

  考虑树上 DP,令 f(u,i) 表示操作完 u 子树内的所有操作后,区间最大值 i 的概率。同时注意到 m 相较于值域大小 109 非常小,所以很多数是不可能成为最大值的。记 u 子树所代表的区间内初始元素的最大值 mxu,不难发现仅有 k[mxu,mxu+m]f(u,k) 有意义,而其余 f(u,k) 要不为 0 要不为 1,没有记录的必要。那么状态就能优化为操作完 u 子树内的所有操作后,区间最大值 i+mxu 的概率,并保证 i[0,m]。转移就简单了:

f(u,i)=pivf(v,mxumxv+i1)+(1pi)vf(v,mxumxv+i)

  注意单独计算 f(u,0),因为其前一项应取 0

  复杂度 O(nlogn+m2)。(前一项为预处理 ST 表复杂度。)

Code

#include <cstdio>
#include <vector>
#include <algorithm>

const int MAXN = 1e5, MAXLG = 16, MAXM = 5000;
int n, m, mxa, a[MAXN + 5], lg[MAXN + 5], st[MAXN + 5][MAXLG + 5];
std::vector<int> tree[MAXM + 5];
double f[MAXM + 5][MAXM + 5];

inline void chkmax ( int& a, const int b ) { if ( a < b ) a = b; }

inline int min_ ( const int a, const int b ) { return a < b ? a : b; }

inline int rint () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

inline int qmax ( const int l, const int r ) {
	int k = lg[r - l + 1], ret = st[l][k];
	return chkmax ( ret, st[r - ( 1 << k ) + 1][k] ), ret;
}

struct Section {
	int l, r, mx; double p;
	inline void read () {
		l = rint (), r = rint (), mx = qmax ( l, r );
		scanf ( "%lf", &p );
	}
	inline bool operator < ( const Section t ) const {
		return l ^ t.l ? l < t.l : r > t.r;
	}
} sec[MAXM + 5];

inline void solve ( const int u ) {
	for ( int v: tree[u] ) solve ( v );
	f[u][0] = 1 - sec[u].p;
	for ( int v: tree[u] ) f[u][0] *= f[v][sec[u].mx - sec[v].mx];
	for ( int i = 1; i <= m; ++ i ) {
		double p = 1, q = 1;
		for ( int v: tree[u] ) {
			p *= f[v][min_ ( sec[u].mx + i - sec[v].mx - 1, m )];
			q *= f[v][min_ ( sec[u].mx + i - sec[v].mx, m )];
		}
		f[u][i] = sec[u].p * p + ( 1 - sec[u].p ) * q;
	}
}

int main () {
	n = rint (), m = rint ();
	for ( int i = 1; i <= n; ++ i ) chkmax ( mxa, a[i] = st[i][0] = rint () );
	for ( int i = 2; i <= n; ++ i ) lg[i] = lg[i >> 1] + 1;
	for ( int j = 1; 1 << j <= n; ++ j ) {
		for ( int i = 1; i + ( 1 << j ) - 1 <= n; ++ i ) {
			chkmax ( st[i][j] = st[i][j - 1], st[i + ( 1 << j >> 1 )][j - 1] );
		}
	}
	for ( int i = 1; i <= m; ++ i ) sec[i].read ();
	sec[++ m] = { 1, n, qmax ( 1, n ), 0.0 };
	std::sort ( sec + 1, sec + m + 1 );
	for ( int i = 2; i <= m; ++ i ) {
		for ( int j = i - 1; j; -- j ) {
			if ( sec[j].l <= sec[i].l && sec[i].r <= sec[j].r ) {
				tree[j].push_back ( i );
				break;
			}
		}
	}
	solve ( 1 );
	double ans = 0;
	for ( int i = 0; i <= m; ++ i ) {
		ans += ( i + mxa ) * ( f[1][i] - f[1][i - 1] );
	}
	printf ( "%.12f\n", ans );
	return 0;
}
posted @   Rainybunny  阅读(109)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示