CF712E [Memort and Casinos]

GreenDuck發表於2019-03-16

題意

每次詢問一段區間[l,r],求從最左邊走到最右邊(r+1)的概率(若走到l-1,則GG了),每個點上寫有向右走的概率。支援單點修改。


 

思考

若只查詢一次,那隻要知道每個點在不走到l-1的情況下,向右移動一格的概率就行了,最後乘起來就是答案。

但我們忽略了一件事情,若從一個區間的某一點出發,從左邊走出去和右邊走出去的概率和為1(不可能停在中間),於是我們要設計一個狀態,並能夠合併,這樣才有可能優化複雜度。

設 [l,r] 區間的L為從第l個點(最左邊)出發,在右邊走出去的概率,R為第R個點出發,在左邊走出去的概率。

[L1-->R1][L2-->R2]
[ L -- -- -- --> R ]

請記住,他們每個的實際意義。因為每個點要麼從左邊走出去,要麼從右邊走出去,所(1-L1)的實際意義就是從(左邊出發,不從右邊走出),即(從左邊出發,從左邊走出去的概率)。

那麼若有兩個連續的區間,合併成一個新區間會有怎樣的答案? 設左右兩個區間的答案分別為L1,R1,L2,R2,則:

 

L   =       L1*L2                                                                      走到右邊還要再走到更右邊

         +   L1(1-L2)(1-R1)*L2                                               第一次向右邊走失敗了,再退回來再向右走

         +   L1(1-L2)(1-R1)(1-L2)(1-R1)*L2                         反反覆覆.......

+.......

整理一下,
                   L   =   L1L2+L1L2(1-L2)(1-R1)+L1L2(1-L2)2+.......
(1-L2)(1-R1)L   =   L1L2(1-L2)(1-R1)+L1L2(1-L2)2+.......

 

作差,

L(1-(1-L2)(1-R1))=L1L2

L=L1L2/(1-(1-L2)(1-R1))

R也可以類似地得到,但注意它們的和不一定為1,因為是兩個不同的端點。

簡單地線段樹維護。


程式碼

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1E5+5;
 4 int n,m;
 5 double x,y,a[maxn];
 6 struct tree{int l,r;double L,R;}t[maxn*4];
 7 tree merge(tree x,tree y)
 8 {
 9     tree z;
10     z.l=x.l;z.r=y.r;
11     z.L=x.L*y.L/(1-(1-y.L)*(1-x.R));
12     z.R=x.R*y.R/(1-(1-y.L)*(1-x.R));//不要認為反一反就對了QAQ 
13     return z;
14 }
15 void build(int l,int r,int num)
16 {
17     t[num].l=l,t[num].r=r;
18     if(l==r)
19     {
20         t[num].L=a[l];
21         t[num].R=1-a[l];
22         return;
23     }
24     int mid=(l+r)>>1;
25     build(l,mid,num*2);build(mid+1,r,num*2+1);
26     t[num]=merge(t[num*2],t[num*2+1]);
27 }
28 void change(int pos,double val,int num)
29 {
30     if(t[num].l==t[num].r)
31     {
32         t[num].L=val;
33         t[num].R=1-val;
34         return;
35     }
36     int mid=(t[num].l+t[num].r)>>1;
37     if(pos<=mid)change(pos,val,num*2);
38     else change(pos,val,num*2+1);
39     t[num]=merge(t[num*2],t[num*2+1]);
40 }
41 tree ask(int L,int R,int num)
42 {
43     if(L<=t[num].l&&t[num].r<=R)return t[num];
44     int mid=(t[num].l+t[num].r)>>1;
45     if(R<=mid)return ask(L,R,num*2);
46     else if(mid<L)return ask(L,R,num*2+1);
47     else return merge(ask(L,R,num*2),ask(L,R,num*2+1));
48 }
49 int main()
50 {
51     ios::sync_with_stdio(false);
52     cin>>n>>m;
53     for(int i=1;i<=n;++i)
54     {
55         cin>>x>>y;
56         a[i]=x/y;
57     }
58     build(1,n,1);
59     while(m--)
60     {
61         int opt,d;
62         cin>>opt;
63         if(opt==1)
64         {
65             cin>>d>>x>>y;
66             change(d,x/y,1);
67         }
68         else
69         {
70             int l,r;
71             cin>>l>>r;
72             cout<<fixed<<setprecision(8)<<ask(l,r,1).L<<endl;
73         }
74     }
75     return 0;
76 }
View Code

 

相關文章