1. 程式人生 > >JeremyGuo膜你賽 t3 azui

JeremyGuo膜你賽 t3 azui

題意:

你有一個長度為 n 的序列,其中第 i 個位置上寫了一個浮點數 pipi,表示你如果 在當前位置,那麼你有 pi 的概率向右走一步,反之(1−pi)的概率向反方向走一步。 定義一個遊戲獲勝的概率為: 一個序列,你一開始站在 1 的位置上,按照上述規則進行移動,如果從序列右端 移動出去了,那麼就算獲勝,如果中途從左端移出,就算失敗,問經過了任意次 的移動後獲勝的概率。 現在有一長度為 n 的序列,m 個操作,操作分為以下兩種: 1、修改一個位置上的 pi 2、詢問如果將某一段[l,r]提取出來單獨做一個遊戲,獲勝的概率是多少?

第一行給出兩個數 n,m 表示序列的長度和操作的數量 接下來 n 行每行包含兩個數 Ai, Bi 這用來表示 pi=Ai/Bi 其中第 i 個表示 pi 接下來 m 行每行第一個數表示操作型別 若為 1,那麼接下來三個數分別表示修改的位置和新的 pi=Ai/Bi 若為 2,那麼接下來兩個證書 L 和 R,表示進行遊戲的區間。 

輸出的行數應該和操作 2 的數量相同,每行一個整數,最後的答案模 9999991 (質數)輸出——就是說分數用整數表示(別告訴我你不知道怎麼搞)。 好吧:pi=Ai*Bi^(-1)=Ai*Bi^(MOD-2)然後你把 pi 當成浮點數就行了,不用管別 的

 保證 1≤ n,m ≤ 200000,並且對於每個點的概率 Ai/Bi 保證 Ai≤Bi≤1000

題解

這道題是一道好題,正解是線段樹。

可以發現,這道題要求單點修改,區間查詢,這些線段樹都可以輕鬆做到,只要你解決了如何維護這顆線段樹,這道題也就相當於解決了

維護線段樹的難度在於區間合併,對於處理這個問題,有兩種思路,暴力列式化簡和列方程

這裡主要介紹一下來自EnderGZM的列方程做法:

首先對於線段樹的每個區間,維護兩個值,一個是最左端走出最右端的Plr,另一個是從最右端走出最左端Prl

為了方便,對於合併的另一個相鄰區間的這兩個值,我們用Qlr和Qrl表示,考慮如何列出方程,見下圖


用P[A]表示從A點走出左區間的概率,P[B],P[C]同理

那麼可以得出如下方程:

P[A]=Plr*P[C] 從A走到C,再從C走出

P[C]=Qlr+(1-Qlr)*P[B] 從C走出分兩種情況,直接走出與往回走到B再走出

P[B]=(1-Prl)*P[C] 從B走到C,再從C走出

於是就可以通過上述方程解出P[A],這也即是合併後區間的Plr

Prl同理可得

這裡直接把結論給出來:

合併後的區間

Plr=plr*qlr/(1-(1-qlr)*(1-prl))

Prl=prl*qrl/(1-(1-qlr)*(1-prl))

於是這道題就可以做了,雖然是資料結構,但是程式碼也並不難實現,只有不到100行

code:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
 
using namespace std;
const int MAXN=200005;
const int mod=9999991;
int n,m,t,A,B,P[MAXN];
struct node{
    int s,e,plr,prl;
}tree[MAXN<<2];
#define lson i<<1
#define rson i<<1|1
int inv[mod+5];
 
inline void prepare(int n){
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    return;
}
inline void Read(int &Ret){
    char ch;bool flag=0;
    for(;ch=getchar(),!isdigit(ch);)if(ch=='-')flag=1;
    for(Ret=ch-'0';ch=getchar(),isdigit(ch);Ret=Ret*10+ch-'0');
    flag&&(Ret=-Ret);
}
inline void pushup(int i){
    int q = 1ll*(1ll-tree[lson].prl+mod)%mod*(1ll-tree[rson].plr+mod)%mod;
    tree[i].plr = 1ll*tree[lson].plr*tree[rson].plr%mod*inv[(1ll-q+mod)%mod]%mod;
    tree[i].prl = 1ll*tree[lson].prl*tree[rson].prl%mod*inv[(1ll-q+mod)%mod]%mod;
}
void build(int i,int s,int e){
    tree[i].s=s; tree[i].e=e;
    if(s==e)
    {
        Read(A); Read(B);
        tree[i].plr=1ll*A*inv[B]%mod;
        tree[i].prl=(mod+1ll-tree[i].plr)%mod;
        return;
    }
    int mid=(s+e)>>1;
    build(lson,s,mid);
    build(rson,mid+1,e);
    pushup(i);
}
int pos,cA,cB;
void update(int i){
    if(tree[i].s==tree[i].e)
    {
        tree[i].plr=1ll*cA*inv[cB]%mod;
        tree[i].prl=(1-tree[i].plr+mod)%mod;
        return;
    }
    if(pos<=tree[lson].e) update(lson);
    else update(rson); pushup(i);
}
int s,e,Plr,Prl;
inline void Merge(int i){
    Plr=1ll*Plr*tree[i].plr%mod*inv[((1ll-(1ll-tree[i].plr)*(1ll-Prl)%mod)+mod)%mod]%mod;
    Prl=1ll*Prl*tree[i].prl%mod*inv[((1ll-(1ll-tree[i].plr)*(1ll-Prl)%mod)+mod)%mod]%mod;
}
void Query(int i){
    if(s<=tree[i].s&&tree[i].e<=e)
        {Merge(i); return;}
    if(s <= tree[lson].e) Query(lson);
    if(e >= tree[rson].s) Query(rson);
}
int main()
{
    Read(n); Read(m);
    prepare(mod); build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        Read(t);
        if(t==1)
        {
            Read(pos); Read(cA); Read(cB);
            update(1);
        }
        else
        {
            Read(s); Read(e);
            Plr=Prl=1; Query(1);
            printf("%d\n",(Plr+mod)%mod);
        }
    }
    return 0;
}