[UOJ299][CTSC2017] 游戏

【CTSC2017】游戏

problem

UOJ299

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} k1s 局的胜负状态。

期望不妨拆成求和每一局获胜的概率。

那么答案为 ∑ 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=1Ak1Ak2...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=1Ak1Ak2...Aks)=P(Ak1...Aks)P(Xi=1Ak1...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(Ak2Ak1)P(Ak3Ak1Ak2)...P(AksAk1...Aks1)

因为每一局的胜负概率只和上一局有关,所以 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(Ak3Ak1Ak2)=P(Ak3Ak2)

即, 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(Ak2Ak1)P(Ak3Ak2)...P(AksAks1)

假设 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(AkjXi=1)P(Xi=1Akj+1)...P(AksAks1)

所以 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=1Ak1Ak2...Aks)=P(Akj+1Akj)P(AkjXi=1)P(Xi=1Akj+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+1Akj)P(AkjXi=1)P(Xi=1Akj+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+11P(Akj+1Akj)P(AkjXi=1)P(Xi=1Akj+1)

Q ( r ∣ l ) : Q(r|l): Q(rl): 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+1l)=p[l+1],再考虑计算 Q ( l + 2 ∣ l ) Q(l+2\Big|l) Q(l+2l)
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+2l)=Q(l+2l+1)Q(l+1l)+Q(l+2(l+1))Q((l+1)l)=p[l+2]p[l+1]+q[l+2](1p[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+2l),Q((l+2)l),Q((l+2)l)

发现,这其实是两个矩阵相乘的结果,即 f l + 1 ⋅ f l + 2 f_{l+1}·f_{l+2} fl+1fl+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=[1qiqi1pipi]

可以继续这么归纳下去,计算 Q ( r ∣ l ) Q(r\Big|l) Q(rl) 等相关信息,无非就是一个连续区间的矩阵相乘后某个位置的结果。

∑ 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+11P(Akj+1Akj)P(AkjXi=1)P(Xi=1Akj+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=flsongrson+glsonfrson;fnow=flsongrson

最后就是具体实现问题了。

考虑插入两个哨兵 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值