【CTSC2017】游戏
problem
solution
定义 X i : X_i: Xi: 当前已知条件第 i i i 局的状态 1 / 0 1/0 1/0(胜/败)。
将 X i = C i X_i=C_i Xi=Ci 记为事件 A i A_i Ai。
假设现在已知条件共有 s s s 个,即:第 k 1 ∼ s k_{1\sim s} k1∼s 局的胜负状态。
期望不妨拆成求和每一局获胜的概率。
那么答案为 ∑ i = 1 n P ( X i = 1 ∣ A k 1 A k 2 . . . A k s ) \sum_{i=1}^nP(X_i=1\Big|A_{k_1}A_{k_2}...A_{ks}) ∑i=1nP(Xi=1∣∣∣Ak1Ak2...Aks)
P ( X i = 1 ∣ A k 1 A k 2 . . . A k s ) = P ( X i = 1 ⋅ A k 1 . . . A k s ) P ( A k 1 . . . A k s ) P(X_i=1\Big|A_{k_1}A_{k_2}...A_{ks})=\frac{P(X_i=1·A_{k_1}...A_{k_s})}{P(A_{k_1}...A_{k_s})} P(Xi=1∣∣∣Ak1Ak2...Aks)=P(Ak1...Aks)P(Xi=1⋅Ak1...Aks)
P ( A k 1 . . . A k s ) = P ( A k 1 ) P ( A k 2 ∣ A k 1 ) P ( A k 3 ∣ A k 1 A k 2 ) . . . P ( A k s ∣ A k 1 . . . A k s − 1 ) P(A_{k_1}...A_{k_s})=P(A_{k_1})P(A_{k_2}|A_{k_1})P(A_{k_3}|A_{k_1}A_{k_2})...P(A_{k_s}|A_{k_1}...A_{k_{s-1}}) P(Ak1...Aks)=P(Ak1)P(Ak2∣Ak1)P(Ak3∣Ak1Ak2)...P(Aks∣Ak1...Aks−1)
因为每一局的胜负概率只和上一局有关,所以 P ( A k 3 ∣ A k 1 A k 2 ) = P ( A k 3 ∣ A k 2 ) P(A_{k_3}|A_{k_1}A_{k_2})=P(A_{k_3}|A_{k_2}) P(Ak3∣Ak1Ak2)=P(Ak3∣Ak2)
即, P ( A k 1 . . . A k s ) = P ( A k 1 ) P ( A k 2 ∣ A k 1 ) P ( A k 3 ∣ A k 2 ) . . . P ( A k s ∣ A k s − 1 ) P(A_{k_1}...A_{k_s})=P(A_{k_1})P(A_{k_2}|A_{k_1})P(A_{k_3}|A_{k_2})...P(A_{k_s}|A_{k_{s-1}}) P(Ak1...Aks)=P(Ak1)P(Ak2∣Ak1)P(Ak3∣Ak2)...P(Aks∣Aks−1)
假设 k 1 < k 2 < . . . < k j < i < k j + 1 < . . . < k s k_1<k_2<...<k_j<i<k_{j+1}<...<k_s k1<k2<...<kj<i<kj+1<...<ks
类似地, P ( X i = 1 A k 1 . . . A k s ) = P ( A k 1 ) . . . P ( A k j ∣ X i = 1 ) P ( X i = 1 ∣ A k j + 1 ) . . . P ( A k s ∣ A k s − 1 ) P(X_i=1A_{k_1}...A_{k_s})=P(A_{k_1})...P(A_{k_j}|X_i=1)P(X_i=1|A_{k_{j+1}})...P(A_{k_s}|A_{k_{s-1}}) P(Xi=1Ak1...Aks)=P(Ak1)...P(Akj∣Xi=1)P(Xi=1∣Akj+1)...P(Aks∣Aks−1)
所以 P ( X i = 1 ∣ A k 1 A k 2 . . . A k s ) = P ( A k j ∣ X i = 1 ) P ( X i = 1 ∣ A k j + 1 ) P ( A k j + 1 ∣ A k j ) P(X_i=1\Big|A_{k_1}A_{k_2}...A_{ks})=\frac{P(A_{k_j}|X_i=1)P(X_i=1|A_{k_{j+1}})}{P(A_{k_{j+1}}|A_{k_j})} P(Xi=1∣∣∣Ak1Ak2...Aks)=P(Akj+1∣Akj)P(Akj∣Xi=1)P(Xi=1∣Akj+1)
答案为 ∑ i = 1 n P ( A k j ∣ X i = 1 ) P ( X i = 1 ∣ A k j + 1 ) P ( A k j + 1 ∣ A k j ) \sum_{i=1}^n\frac{P(A_{k_j}|X_i=1)P(X_i=1|A_{k_{j+1}})}{P(A_{k_{j+1}}|A_{k_j})} ∑i=1nP(Akj+1∣Akj)P(Akj∣Xi=1)P(Xi=1∣Akj+1)
也就是说,已知结果将 n n n 局游戏划分成了若干段区间,每段的贡献计算的先决条件都是一样的。
也就是说每个区间的答案为 ∑ i = k j + 1 i = k j + 1 − 1 P ( A k j ∣ X i = 1 ) P ( X i = 1 ∣ A k j + 1 ) P ( A k j + 1 ∣ A k j ) \sum_{i=k_j+1}^{i=k_{j+1}-1}\frac{P(A_{k_j}|X_i=1)P(X_i=1|A_{k_{j+1}})}{P(A_{k_{j+1}}|A_{k_j})} ∑i=kj+1i=kj+1−1P(Akj+1∣Akj)P(Akj∣Xi=1)P(Xi=1∣Akj+1)
设 Q ( r ∣ l ) : Q(r|l): Q(r∣l): 第 l l l 局 R R R 赢的情况下,第 r r r 局 R R R 赢的概率, ∼ l \sim l ∼l 则表示第 l l l 局 R R R 输。
显然有,
Q
(
l
+
1
∣
l
)
=
p
[
l
+
1
]
Q(l+1\Big|l)=p[l+1]
Q(l+1∣∣∣l)=p[l+1],再考虑计算
Q
(
l
+
2
∣
l
)
Q(l+2\Big|l)
Q(l+2∣∣∣l)。
Q
(
l
+
2
∣
l
)
=
Q
(
l
+
2
∣
l
+
1
)
⋅
Q
(
l
+
1
∣
l
)
+
Q
(
l
+
2
∣
∼
(
l
+
1
)
)
⋅
Q
(
∼
(
l
+
1
)
∣
l
)
=
p
[
l
+
2
]
⋅
p
[
l
+
1
]
+
q
[
l
+
2
]
⋅
(
1
−
p
[
l
+
1
]
)
Q(l+2\Big|l)=Q(l+2\Big|l+1)·Q(l+1\Big|l)+Q(l+2\Big|\sim(l+1))·Q(\sim(l+1)\Big|l)\\=p[l+2]·p[l+1]+q[l+2]·(1-p[l+1])
Q(l+2∣∣∣l)=Q(l+2∣∣∣l+1)⋅Q(l+1∣∣∣l)+Q(l+2∣∣∣∼(l+1))⋅Q(∼(l+1)∣∣∣l)=p[l+2]⋅p[l+1]+q[l+2]⋅(1−p[l+1])
同理可计算出,
Q
(
l
+
2
∣
∼
l
)
,
Q
(
∼
(
l
+
2
)
∣
l
)
,
Q
(
∼
(
l
+
2
)
∣
∼
l
)
Q(l+2\Big|\sim l),Q(\sim (l+2)\Big|l),Q(\sim (l+2)\Big|\sim l)
Q(l+2∣∣∣∼l),Q(∼(l+2)∣∣∣l),Q(∼(l+2)∣∣∣∼l)
发现,这其实是两个矩阵相乘的结果,即 f l + 1 ⋅ f l + 2 f_{l+1}·f_{l+2} fl+1⋅fl+2
f i = [ 1 − q i q i 1 − p i p i ] f_{i}=\begin{bmatrix}1-q_i\quad\quad q_i\\1-p_i\quad\quad p_i\end{bmatrix} fi=[1−qiqi1−pipi]
可以继续这么归纳下去,计算 Q ( r ∣ l ) Q(r\Big|l) Q(r∣∣∣l) 等相关信息,无非就是一个连续区间的矩阵相乘后某个位置的结果。
∑ i = k j + 1 i = k j + 1 − 1 P ( A k j ∣ X i = 1 ) P ( X i = 1 ∣ A k j + 1 ) P ( A k j + 1 ∣ A k j ) \sum_{i=k_j+1}^{i=k_{j+1}-1}\frac{P(A_{k_j}|X_i=1)P(X_i=1|A_{k_{j+1}})}{P(A_{k_{j+1}}|A_{k_j})} ∑i=kj+1i=kj+1−1P(Akj+1∣Akj)P(Akj∣Xi=1)P(Xi=1∣Akj+1),用线段树维护矩阵 f f f。
分母就是 f f f 矩阵从 k j k_{j} kj 一直乘到 k j + 1 k_{j+1} kj+1。
分子就是 f f f 矩阵乘到 i i i 位置时,只乘第二列(表示胜利),可以新定义一个矩阵 g i g_i gi。
g i = [ 0 q i 0 p i ] g_{i}=\begin{bmatrix}0\quad\quad q_i\\0\quad\quad p_i\end{bmatrix} gi=[0qi0pi],在乘到 i i i 位置时变成乘 g i g_i gi。
同样用线段树维护, g n o w = f l s o n ∗ g r s o n + g l s o n ∗ f r s o n ; f n o w = f l s o n ∗ g r s o n g_{now}=f_{lson}*g_{rson}+g_{lson}*f_{rson};f_{now}=f_{lson}*g_{rson} gnow=flson∗grson+glson∗frson;fnow=flson∗grson
最后就是具体实现问题了。
考虑插入两个哨兵 0 , n + 1 0,n+1 0,n+1,初始局面答案就是一整个区间。
有涉及到询问当前已知条件中的前驱
l
l
l 后继
r
r
r 问题,就需要用 STL
实现。
加点就用答案减去区间 ( l , r ) (l,r) (l,r) 的贡献,再加上区间 ( l , i ) (l,i) (l,i) 和区间 ( i , r ) (i,r) (i,r) 的贡献。
删点就用答案减去区间 ( l , i ) (l,i) (l,i) 和区间 ( i , r ) (i,r) (i,r) 的贡献,再加上区间 ( l , r ) (l,r) (l,r) 的贡献。
code
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct matrix {
double c[2][2];
// double * operator [] ( int x ) { return c[x]; }
matrix() { memset( c, 0, sizeof( c ) ); }
friend matrix operator + ( matrix u, matrix v ) {
matrix ans;
for( int i = 0;i < 2;i ++ )
for( int j = 0;j < 2;j ++ )
ans.c[i][j] = v.c[i][j] + u.c[i][j];
return ans;
}
friend matrix operator * ( matrix u, matrix v ) {
matrix ans;
for( int i = 0;i < 2;i ++ )
for( int j = 0;j < 2;j ++ )
for( int k = 0;k < 2;k ++ )
ans.c[i][j] += u.c[i][k] * v.c[k][j];
return ans;
}
void print() {
for( int i = 0; i < 2; i ++ ) {
for( int j = 0; j < 2; j ++ )
printf( "%.3f ", c[i][j] );
printf( "\n" );
}
}
};
#define maxn 200005
struct node { matrix f, g; }t[maxn << 2];
int n, m;
char type;
double ans;
double p[maxn], q[maxn];
map < int, bool > x;
#define lson now << 1
#define rson now << 1 | 1
#define mid ( ( l + r ) >> 1 )
node operator + ( node x, node y ) {
node ans;
ans.f = x.f * y.f;
ans.g = x.g * y.f + x.f * y.g;
return ans;
}
void build( int now, int l, int r ) {
if( l == r ) {
t[now].f.c[0][0] = 1 - q[l];
t[now].f.c[0][1] = t[now].g.c[0][1] = q[l];
t[now].f.c[1][0] = 1 - p[l];
t[now].f.c[1][1] = t[now].g.c[1][1] = p[l];
// printf( "(%d):\n", l );
// t[now].f.print();t[now].g.print();
return;
}
build( lson, l, mid );
build( rson, mid + 1, r );
t[now] = t[lson] + t[rson];
// printf( "{ %d } [%d, %d] ::\n", now, l, r );
// t[now].f.print(); t[now].g.print();
}
node query( int now, int l, int r, int L, int R ) {
if( L <= l and r <= R ) return t[now];
if( R <= mid ) return query( lson, l, mid, L, R );
else if( mid < L ) return query( rson, mid + 1, r, L, R );
else return query( lson, l, mid, L, R ) + query( rson, mid + 1, r, L, R );
}
double Ask( int l, int r ) {
node now = query( 1, 0, n + 1, l + 1, r );
// now.f.print(); now.g.print();
return now.g.c[x[l]][x[r]] / now.f.c[x[l]][x[r]];
}
int main() {
scanf( "%d %d %c %lf", &n, &m, &type, &p[1] );
for( int i = 2;i <= n;i ++ ) scanf( "%lf %lf", &p[i], &q[i] );
p[0] = q[0] = x[0] = 1, x[n + 1] = 0;
build( 1, 0, n + 1 );
ans = Ask( 0, n + 1 );
// printf( "%f\n", ans );
while( m -- ) {
char opt[10]; int i, c;
scanf( "%s", opt );
if( opt[0] == 'a' ) {
scanf( "%d %d", &i, &c );
auto r = x.lower_bound( i );
auto l = r;
l --;
x[i] = c;
ans -= Ask( l -> first, r -> first );
ans += Ask( l -> first, i );
ans += Ask( i, r -> first );
}
else {
scanf( "%d", &i );
auto r = x.upper_bound( i );
auto l = r;
l --, l --;
ans -= Ask( l -> first, i );
ans -= Ask( i, r -> first );
ans += Ask( l -> first, r -> first );
x.erase( ++ l );
}
printf( "%f\n", ans );
}
return 0;
}