[TsinsenA1490] osu!(喬明達)

作爲一名osu!玩家,這道題成功吸引到了我。。。

題意

長度爲n的序列,給出每一個數字可能爲1的概率ai ,每個數字爲0的概率爲1ai 。兩個操作:修改某個數字的概率,詢問一段區間得分期望,得分計算方式如下。
將玩家完成一張地圖的01串中所有的0刪去,則這個串可能會斷裂成若干段連續的1。對於一段長度爲L1L1 ,你的總分會增加L2+L+1 。例如:一張地圖有10個音,某玩家完成情況爲1011101110,則刪除所有0後得到的是“1”“111”和“111”。因此這個玩家的得分爲(1+1+1)+(9+3+1)+(9+3+1)=29

題解

這是出題人的題解

下面簡述此題解內容。

分值的計算爲mi=1(L2i+Li+1)m 爲極大連續1子段的段數,leni 爲第i段的長度。

將其分爲三部分分別計算期望:L2i,Li1 。將這三部分分別記爲S2,S1S0 。記01串第i 位爲bi ,則bi=1 概率爲aibi=0 概率1ai

S1 的期望

E(S1)=E(i=1nbi)=i=1nE(bi)=i=1nai

複雜度O(n)

S0 的期望
S0 的實際意義是01串中極大1字段的個數。關注每個這樣子串的起始位置,這樣的位置和子串是一一對應的,只需要統計有多少位置是子串的起始位置。
考慮第i 位,它是起始位置當且僅當第i 位是1,第i1 位是0。特別地,規定第0位一定是0,即a0=0 。因此第i 位是起始位置的概率爲ai(1ai1)

E(S0)=E(i=1nai(1ai1))

複雜度O(n)

E(S2) 的計算
由於出現了平方,計算S2 的期望並沒有計算S0S1 那麼簡單。
定義兩個數列 {leni}{expi}leni 表示{bi} 構成的01串最長的全是1的後綴的長度,expi 表示{bi} 構成的01串S2 的期望值。len0=0,exp0=0
對於leni ,如果bi=1 ,則leni=leni1+1 ,否則leni=0 ,故

leni=ai(leni1+1)+(1ai)0=ai(leni1+1)

對於expi ,如果bi=1Δ=(leni1+1)2len2i1=2leni1+1 ,否則Δ=0 ,故
expi=expi1+ai(2leni1+1)+(1ai)0=expi1+ai(2leni1+1)

複雜度O(n)

用線段樹維護信息與查詢

維護S1
S1=L.S1+R.S1

維護S0
Lai 值分別爲a,b,c,Rai 值分別爲d,e,f.
L.S0=a+b(1a)+c(1b)
R.S0=d+e(1+d)+f(1e)
S0=a+b(1a)+c(1b)+d(1c)+e(1d)+f(1e)=L.S0+R.S0cd
故記錄區間左右端點的ai 值就可以維護S0 了。

維護S2
leni=aileni1+ai
expi=expi1+2aileni1+ai
這是一個線性變換,表示矩陣爲

T=ai0ai2ai1ai001

(leni1,expi1,1)T=(leni,expi,1)
所以拿(0,0,1) 去乘一整個區間的矩陣就能得到(lenk,expk,1)k 爲區間長度,expkS2 的答案。
這道題沒有區間修改不用打標記,每個區間直接記錄矩陣,由於矩陣乘法的結合律,可以用線段樹維護區間的矩陣乘積。但是直接這麼寫常數太大。
觀察T
a0cb1d001e0gf1h001=ae0ce+gafb1cf+d+h001

可以看到一個矩陣我們只需記錄四個值即可,大大減小了常數。

複雜度O(n+mlogn)

這道題還有個簡化版本,在BZOJ4318,給出PO姐的題解

代碼

/// by ztx
/// blog.csdn.net/hzoi_ztx

#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN  500010LL
#define  kT  2000010LL
#define  M     ((L+R)/2)
#define  l(o)  (o<<1)
#define  r(o)  (o<<1|1)
#define  left  l(o),L,M
#define  right r(o),M+1,R

struct mat { lf x[4]; };
inline mat Mul(const mat&a,const mat&b) {
    return (mat){a.x[0]*b.x[0],a.x[0]*b.x[1]+a.x[1],a.x[2]*b.x[0]+b.x[2],a.x[2]*b.x[1]+a.x[3]+b.x[3]};
}
inline void One(mat&a) {
    a.x[0] = 1.0, a.x[1] = a.x[2] = a.x[3] = 0;
}
inline lf Ans(const mat&a) {
    return a.x[3];
//    (0,0,1) * (a b 0)  = (c,d,1)
//              (0 1 0)       ^
//              (c d 1)
}

int n, ql, qr;
lf a[kN], qw, qa0, qa1, qra;
mat qa2;

lf s0[kT], s1[kT], la[kT], ra[kT];
mat t[kT];

void update(int o) {
    la[o] = la[l(o)], ra[o] = ra[r(o)];
    s0[o] = s0[l(o)]+s0[r(o)]-ra[l(o)]*la[r(o)];
    s1[o] = s1[l(o)]+s1[r(o)];
    t[o] = Mul(t[l(o)],t[r(o)]);
}

void Build(int o=1, int L=1, int R=n) {
    if (L == R) {
        t[o].x[0] = t[o].x[2] = t[o].x[3] = a[L], t[o].x[1] = a[L]*2;
        la[o] = ra[o] = s0[o] = s1[o] = a[L];
        return ;
    }
    Build(left), Build(right);
    update(o);
}

void Modify(int o=1, int L=1, int R=n) {
    if (L == R) {
        a[L] = qw;
        t[o].x[0] = t[o].x[2] = t[o].x[3] = qw, t[o].x[1] = qw*2;
        la[o] = ra[o] = s0[o] = s1[o] = qw;
        return ;
    }
    if (ql <= M) Modify(left);
    else Modify(right);
    update(o);
}

void Query(int o=1, int L=1, int R=n) {
    if (ql<=L && R<=qr) {
        qa0 += s0[o]-qra*la[o], qra = ra[o];
        qa1 += s1[o];
        qa2 = Mul(qa2,t[o]);
        return ;
    }
    if (ql <= M) Query(left);
    if (qr > M) Query(right);
}

#undef r
#define r(x)   read(x)
int main() {
    int m, i, ope;
    r(n), r(m);
    Rep (i,1,n) scanf("%lf", &a[i]);
    Build();
    while (m --> 0) {
        r(ope);
        if (ope) r(ql), scanf("%lf", &qw), Modify();
        else r(ql), r(qr), qa0=qa1=qra=0, One(qa2), Query(), printf("%.2f\n", qa0+qa1+Ans(qa2));
    }
    END: getchar(), getchar();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章