Omeed 線段樹

題面






2.12 - - -

題解

大概還是挺妙的?
首先基礎分和連擊分互不干擾,所以可以分開統計。
基礎分的統計比較簡單,等於:
\[A \sum_{i = l}^{r} p_i\]
連擊分的統計就比較複雜了,因爲是求期望,根據期望的線性性,我們可以先算出\(f_i\)表示每個音符的期望連擊分,再計算整個區間的期望連擊分。
觀察連擊分的統計方法,可以知道,區間其實是互不干擾的,也就是說,每個區間中的期望連擊分,其實都是在對進入這個區間時的期望連擊分\(f_{l - 1}\)的一個疊加和增幅。
考慮區間的期望連擊分可以表示爲:
\[B \sum_{i = l}^r p_i (f_{i - 1} + 1)\]
因爲只有這次打到了完美纔可以計入這個音符的貢獻,所以這次的貢獻是建立在當前音符完美的情況下的,所以貢獻就是\(p_i(f_{i - 1} + 1)\)了。
考慮\(f_i\)如何轉移。
\[f_i = p_i(f_{i - 1} + 1) + (1 - p_i)f_{i - 1}t\]
\[ = (p_i + t(1 - p_i))f_{i - 1} + p_i\]
觀察到這是一個類似於\(kx + b\)的形式,因此對於一個\(f_i\),如果一個\(j\)滿足\(j \le i\),那麼一定可以表示爲\(f_i = kf_j + b\)的形式。
那麼對於區間\([l, r]\),因爲其中每個\(f_i\),都可以表示爲類似\(kf_{l - 1} + b\)的形式,因此,這個區間的連擊分也一定可以表示爲\(kf_{l - 1} + b\)的形式。
因此我們考慮線段樹,對於區間\([l, r]\)我們維護5個變量,\(k, b, sumb, sumk, sump\),其中\(sump\)是用來算基礎分的,\(sumb, sumk\)就是區間連擊分的係數,\(k, b\)則是\(f_r = kf_{l - 1} + b\)中的\(k\)\(b\).
因爲\(B\)是對於整個區間的係數,因此我們可以先不考慮它,直接統計後面的部分,最後再乘上\(B\)即可。
因此我們考慮如何合併2個區間\([l, mid], [mid + 1, r]\).
根據前面的推導,現在有
\[f_{mid} = k_l f_{l - 1} + b_l, \quad f_{r} = k_r f_{mid} + b_r\]
現在要合併這2個變量,我們只需要把後者表示爲\(kf_{l - 1} + b\)的形式即可。
所以直接把\(f_{mid}\)帶入後面的等式化簡就行了,化簡出來新變量的\(k = k_l k_r, \quad b = k_rb_l + b_r\)
然後來考慮合併區間信息:
現在我們有:
\[sumk_l f_{l - 1} + sumb_l\]
\[sumk_r f_{mid} + sumb_r\]
我們現在要得到的新區間應該要形如第一個區間的樣子,因爲第一個區間已經是這樣了,所以我們只需要轉化一下第二個區間,然後和第一個區間加在一起就行了。
我們直接帶入上面的\(f_{mid} = k_l f_{l - 1} + b_l\),然後化簡併和第一個區間的式子加在一起,最後得到新的\(sumk = sumk_r k_l + sumk_l, \quad sumb = sumk_r b_l + sumb_r + sumb_l\)
最後
\[ans[l][r] = B \cdot sumb[l][r] + A \cdot sump[l][r]\]

代碼

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 501000
#define ac 2001000
#define p 998244353
#define mo(x) ((x) % p)
#define mul(a, b) (1LL * (a) * (b) % p)//error !!!都要用(a), (b)...啊
#define h(x, y) (mul((x), qpow((y), p - 2)))

int n, m, t, A, B;
int pi[AC];

struct node{
    int sumk, sumb, k, b, sump;
}tree[ac];

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

inline void up(int &a, int b) {a += b; if(a < 0) a += p; if(a >= p) a -= p;}
inline int ad(int a, int b) {a += b; if(a < 0) a += p; if(a >= p) a -= p; return a;}

inline int qpow(int x, int have)
{
    int rnt = 1;
    while(have)
    {
        if(have & 1) rnt = mul(rnt, x);
        x = mul(x, x), have >>= 1;
    }
    return rnt;
}

void pre()
{
    n = read();//對於正解來說沒有什麼用的輸入
    n = read(), m = read();
    int a = read(), b = read();
    t = h(a, b), A = read(), B = read();
    for(R i = 1; i <= n; i ++) a = read(), b = read(), pi[i] = h(a, b);
        
}

node merge(node ll, node rr)
{
    node x;
    x.k = mul(ll.k, rr.k), x.b = ad(mul(rr.k, ll.b), rr.b);
    x.sumk = ad(mul(rr.sumk, ll.k), ll.sumk);
    x.sumb = ad(mul(rr.sumk, ll.b), ad(rr.sumb, ll.sumb));  
    x.sump = ad(ll.sump, rr.sump);
    return x;
}

#define update(x) tree[x] = merge(tree[x << 1], tree[(x << 1) + 1]);

node make(int now)
{
    node x;
    x.k = ad(pi[now], mul(t, 1 - pi[now]));
    x.b = x.sumk = x.sumb = x.sump = pi[now];
    return x;
}

void build(int x, int ll, int rr)
{
    if(ll == rr) {tree[x] = make(ll); return ;}
    int mid = (ll + rr) >> 1;
    build(x << 1, ll, mid), build((x << 1) + 1, mid + 1, rr);
    update(x);
}

void change(int x, int l, int r, int w)
{
    if(l == r) {tree[x] = make(w); return ;}
    int mid = (l + r) >> 1;
    if(w <= mid) change(x << 1, l, mid, w);
    else change((x << 1) + 1, mid + 1, r, w);
    update(x);
}

node find(int x, int l, int r, int ll, int rr)
{
    if(l == ll && r == rr) return tree[x];  
    int mid = (l + r) >> 1;
    if(rr <= mid) return find(x << 1, l, mid, ll, rr);
    else if(ll > mid) return find((x << 1) + 1, mid + 1, r, ll, rr);
    else 
    {
        node a = find(x << 1, l, mid, ll, mid);
        node b = find((x << 1) + 1, mid + 1, r, mid + 1, rr);
        return merge(a, b);
    }
}

void work()
{
    for(R i = 1; i <= m; i ++)
    {
        int o = read();
        if(!o) 
        {
            int x = read(), a = read(), b = read();
            pi[x] = h(a, b), change(1, 1, n, x);
        }
        else
        {
            int ll = read(), rr = read();
            node x = find(1, 1, n, ll, rr);
            //int ans = mul(ad(mul(x.sumk, pi[ll]), ad(x.sumb, pi[ll])), B);
            int ans = mul(x.sumb, B);
            up(ans, mul(A, x.sump));
            printf("%d\n", ans);
        }
    }
}

int main()
{
    freopen("in.in", "r", stdin);
    pre();
    build(1, 1, n);
    work();
    fclose(stdin);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章