衝刺專題之 \(DP\)
\(T_A\) Helping People
$$codeforces$$
題意
給定一個長為 \(n\) 序列 \(A\) , 其中有 \(q\) 個區間 \([l , r]\) 定義為 \(B_i\) , 對於每一個區間 , 其有 \(P_i\) 的機率使 \(A\) 序列從 \(l\) 到 \(r\) 之間的數增加 \(1\) . 問最後整個區間的最大值的期望。其中 , \(n \le 10^5 , q \le 5 \times 10^3\)
對於 \(\forall B_i , B_j (1 \le i , j \le q 且 i \ne j)\) $B_i \cap B_j = \varnothing 或 B_i \subseteq B_j 或 B_j \subseteq B_i $ (即對於兩個區間來說,其不相交或被包含)
樣例輸入,輸出
3 5
1 2 3
1 3 0.500
2 2 0.250
1 2 0.800
1 1 0.120
2 2 0.900
4.465000000
本題為 \(SPJ\) , 只要輸出與正解之差 \(\le 10^{-6}\) 即可透過。
題解
一言難盡。
現在我們發現這個區間的性質完全滿足建樹的條件,於是我們建樹,並跑樹形 \(DP\)
並設一個超級原點 \(0\) 為 \([1 , n]\) , 其增加的機率為 \(0\)
我們設 \(front_i\) 為區間 \(i\) 的區間最大值。
現在考慮設 \(dp_{i , j}\) 為第 \(i\) 號節點 , 最大值為 \(front_i + j\) 的 \(\bf{機率}\) (本題的期望就是個幌子)
設 \(sum_{i , j}\) 為 \(dp_i\) 陣列的字首和。即 \(\le front_i + j\) 的總機率。
若我們列舉到 \(x\) 號節點 :
第一步
我們現在不考慮 \(x\) 會增加 \(1\) :
設 \(mid_{i , j}\) 陣列的意義為第 \(i\) 號節點 , 不考慮 \(i\) 會增加 \(1\) , 最大值為 \(front_i + j\) 的機率 .
得到初步轉移:
定義 \(SUM\) 初為 : \(\prod\limits_{z \in son_x}sum[ \ z \ ][ \ front_x + i - front_z \ ]\)
(對於每一個 \(y\) 的運算之後 \(SUM\) 進行此次運算 )
當然,如果 \(sum[ \ y \ ][ \ front_x + i - front_y \ ] = 0\) 的話 , 此時直接為 \(0\) (別問,問就是沒注意錯了 \(114514\) 遍,每次 \(\color{red}{WA}\) \(Test \ 20\) )
考慮此式的正確性:
\(\prod\limits_{z \in son_x}sum[ \ z \ ][ \ front_x + i - front_z \ ]\) : 這個東西意味著 對於 \(x\) 的每一個兒子 , 取的值均 \(\le front_x + i\) 的機率 ;
\(\frac{ SUM }{ sum[ \ y \ ][ \ front_x + i - front_y \ ] }\) : 是不考慮 \(y\) 區間 , 其他區間最大值均 \(\le front_x + i\) 的機率 ;
我們 \(\times mid[ \ y \ ][ \ front_x + i - front_y \ ]\) 保證了整個 \(x\) 區間 最大值為 \(y\) 區間的 \(front_x + i\)
但如果 \(SUM\) 不變的話 , 對於兩個區間 \(y , z \in son_x\) 均有最大值是 \(front_x + i\) 的情況時,
那麼 \(y , z\) 均為 \(front_x + i\) 時的機率將會被計算兩次。
此句減的操作是去重的過程。
第二步
我們進行考慮父區間 \(x\) 對於答案的影響。
我們發現當 \(x\) 的最大值和其某個子區間相同時 , \(i = 1 | \ | 0\) 的時候需要分討一下 (原因下面提到)
對於 \(i \in [2 \ , \ q]\) 時顯然不用再考慮那些被 \(x\) 包含卻未被 \(y \in son_x\) 包含的點中有最大值。因為即使 \(x\) 選 , 也只會把上面的點變為 \(front_x + 1\) ,不會變為 \(front_x + i\) .
我們得到這樣的式子:
顯然
但是對於 $ i = 1 , i = 0$ 情況 , 我們需要對 \(x\) 區間進行分討了。
如果 \(x\) 區間的最大值不在其子節點區間裡:
如果 \(i = 1\) 時 , \(x\) 區間進行加一時 , 變為 \(front_x + 1\) , 最大值可能是他 , 所以這個就保證了區間最大值為 \(front_x + 1\) , 所以剩下的子區間可選的得到的機率為 :
注意你會 \(+ 1\) 的 , 所以不能超出 \(front_x\) .
對於 \(i = 0\) 時 , 因為最大值就在已定了,所以剩下的只要選的不超過 \(front_x\) 就可以了.
如果 \(front_x\) 出現在其子區間裡:
易得:
\(code\)
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e5 + 100 ;
const int M = 5e3 + 100 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while ( c < '0' || c > '9' ) {
f = c == '-' ? -f : f ;
c = getchar() ;
}
while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
int st[N][20] , lg[N] ;
inline void build_ST( int n ) {
for ( int i = 2 ; i <= n ; ++ i ) {
lg[i] = lg[i >> 1] + 1 ;
}
for ( int j = 1 ; j <= lg[n] ; ++ j ) {
for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; ++ i ) {
st[i][j] = max( st[i][j - 1] , st[i + ( 1 << ( j - 1 ) )][j - 1] ) ;
}
}
}
inline int MAX_ST( int l , int r ) {
int k = lg[r - l + 1] ;
return max( st[l][k] , st[r - ( 1 << k ) + 1][k] ) ;
}
class edge {
public:
int to , next ;
}e[M] ; int head[M] , cnt ; bool vis[M] ;
inline void add( int x , int y ) {
cnt ++ ;
e[cnt].to = y ;
e[cnt].next = head[x] ;
head[x] = cnt ;
}
int n , q ;
double dp[M][M] , sum[M][M] ;
class Node {
public:
int l , r , front ;
double p ;
}b[M] ;
bool cmp( Node a , Node b ) {
if ( a.l == b.l ) return a.r > b.r ;
return a.l < b.l ;
}
void dfs_find( int x ) {
for ( int i = x + 1 ; i <= q ; ++ i ) {
if ( b[x].r < b[i].l ) break ;
if ( b[x].l <= b[i].l && b[i].r <= b[x].r && !vis[i] ) {
vis[i] = 1 ;
add(x,i) ; dfs_find(i) ;
}
}
}
void dfs( int x ) {
bool flag = 0 ;
for ( int i = head[x] ; i ; i = e[i].next ) {
dfs(e[i].to) ;
if ( b[x].front == b[e[i].to].front ) flag = 1 ;
}
long double sum0 = 1 ; bool flak = 0 ;
for ( int i = 0 ; i <= q ; ++ i ) {
long double sumi = 1 ; flak = 0 ;
for ( int j = head[x] ; j ; j = e[j].next ) {
int y = e[j].to ;
if ( b[x].front + i - b[y].front <= q )
if ( sum[y][b[x].front + i - b[y].front] > 0 )
sumi *= sum[y][b[x].front + i - b[y].front] ;
else flak = 1 ;
}
if ( !flak )
for ( int j = head[x] ; j ; j = e[j].next ) {
int y = e[j].to ;
if ( b[x].front + i - b[y].front <= q )
if ( sum[y][b[x].front + i - b[y].front] > 0 ) {
dp[x][i] += sumi / sum[y][b[x].front + i - b[y].front] * dp[y][b[x].front + i - b[y].front] ;
sumi -= sumi / sum[y][b[x].front + i - b[y].front] * dp[y][b[x].front + i - b[y].front] ;
}
}
}
for ( int i = q ; i >= 2 ; -- i ) {
dp[x][i] = dp[x][i - 1] * b[x].p + dp[x][i] * ( 1 - b[x].p ) ;
}
if ( !flag ) {
long double sum_0 = 1 ;
for ( int i = head[x] ; i ; i = e[i].next ) {
int y = e[i].to ;
if ( b[x].front - b[y].front <= q ) {
sum_0 *= sum[y][b[x].front - b[y].front] ;
}
}
dp[x][1] = ( 1 - b[x].p ) * dp[x][1] + b[x].p * sum_0 ;
dp[x][0] = ( 1 - b[x].p ) * sum_0 ;
}
else {
dp[x][1] = dp[x][0] * b[x].p + dp[x][1] * ( 1 - b[x].p ) ;
dp[x][0] = dp[x][0] * ( 1 - b[x].p ) ;
}
sum[x][0] = dp[x][0] ;
for ( int i = 1 ; i <= q ; ++ i ) {
sum[x][i] = sum[x][i - 1] + dp[x][i] ;
}
}
inline void Query_Expect() {
long double ans = 0 ;
int maxl = b[0].front ;
for ( int i = 0 ; i <= q ; ++ i ) {
ans += ( maxl + i ) * dp[0][i] ;
}
printf( "%.9Lf\n" , ans ) ;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w" ,stdout ) ;
#endif
n = read() ; q = read() ;
for ( int i = 1 ; i <= n ; ++ i ) {
st[i][0] = read() ;
}
build_ST( n ) ;
for ( int i = 1 ; i <= q ; ++ i ) {
b[i].l = read() ; b[i].r = read() ;
b[i].front = MAX_ST( b[i].l , b[i].r ) ;
scanf( "%lf" , &b[i].p ) ;
}
sort( b + 1 , b + q + 1 , cmp ) ;
b[0].l = 1 , b[0].r = n , b[0].p = 0 ; vis[0] = 1 ;
b[0].front = MAX_ST( 1 , n ) ;
dfs_find(0) ; dfs(0) ;
Query_Expect() ;
}