Solution -「CF 494C」Helping People
\(\mathcal{Description}\)
Link.
給定序列 \(\{a_n\}\) 和 \(m\) 個操作,第 \(i\) 個操作有 \(p_i\) 的概率將 \([l_i,r_i]\) 內的元素 \(+1\)。且保證任意兩個區間要麼不交,要麼有包含關係。求所有操作完成後序列最大值的期望。
\(n\le10^5\),\(m\le5000\)。
\(\mathcal{Solution}\)
首先應當知道,\(E(\max\{a_i\})\not=\max\{E(a_i)\}\)(不然還需要做嘛 qwq),這是由於每個數的期望值是不獨立的。
從題目奇怪的限制入手——各區間構成樹形關係,整個序列上的區間構成一片森林。不妨加入第 \(m+1\)
考慮樹上 DP,令 \(f(u,i)\) 表示操作完 \(u\) 子樹內的所有操作後,區間最大值 \(\le i\) 的概率。同時注意到 \(m\) 相較於值域大小 \(10^9\) 非常小,所以很多數是不可能成為最大值的。記 \(u\) 子樹所代表的區間內初始元素的最大值 \(mx_u\),不難發現僅有 \(k\in[mx_u,mx_u+m]\) 的 \(f(u,k)\) 有意義,而其餘 \(f(u,k)\) 要不為 \(0\) 要不為 \(1\),沒有記錄的必要。那麼狀態就能優化為操作完 \(u\)
\[f(u,i)=p_i\prod_vf(v,mx_u-mx_v+i-1)+(1-p_i)\prod_vf(v,mx_u-mx_v+i) \]
注意單獨計算 \(f(u,0)\),因為其前一項應取 \(0\)。
複雜度 \(\mathcal O(n\log n+m^2)\)。(前一項為預處理 ST 表複雜度。)
\(\mathcal{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; }