[ZJOI2019]線段樹


首先要注意到復制出的兩顆棵段樹一個被修改一個不被修改

所以實際上就是枚舉了是否進行每個操作的\(2^m\)種情況

肯定不可能枚舉出來

我們考慮對每個節點分別統計答案

\(f_x\)表示當前節點\(x\)的所有情況下的\(Tag\)

考慮每次加入一個操作對其\(Tag\)的影響

設當前為第\(t\)個操作

即修改的\(2^{t-1}\)棵線段樹中

有多少棵的\(x\)節點的\(Tag\)\(1\)

一共只有\(4\)種情況

  1. \(Tag\)直接被賦值為\(1\)(在這個點打標記)
  2. \(Tag\)直接被賦值為\(0\)(對該點進行了pushdown操作)
  3. \(Tag\)沒有被修改(根本沒有訪問這個節點)
  4. \(Tag\)繼承自父親節點(對該點的父節點進行了pushdown操作但沒有修改這個節點)

考慮\(f_x\)的改變量

對於前\(3\)種情況

\(f_x\)的改變是明顯的

第一種\(f_x+=2^{t-1}\)

第二種\(f_x+=0\)

第三種\(f_x*=2\)

第四種比較復雜

\(f_x+=使這個點到根有至少一個節點的Tag為1的方案數\)

注意自己有標記也是可以的

因為只有\(1\)的標記會下傳

設這個量為\(g_x\)

考慮\(g_x\)的維護

如果在線段樹上對該點進行了pushdown操作

那么新增的\(2^{t-1}\)棵樹的這個節點根的路徑上都不會有\(Tag=1\)

\(g_x+=0\)

如果在這個點打了標記

這個點和它的子樹到根的路徑上都一定有\(Tag=1\)

這些節點的\(g+=2^{t-1}\)

剩下的節點到根的路徑上的\(Tag\)情況不會改變

\(g_x*=2\)

考慮用線段樹模擬維護\(f\)\(g\)

因為涉及未訪問節點

可以不修改這些節點

把訪問的節點的值除\(2\)

算答案的時候乘上\(2^t\)

但這樣\(g\)的修改不是很好維護

考慮每次修改的同時都會將其除\(2\)

所以用一個\(tag\)記錄被修改了幾次

pushdown時將\(g_{son}\)除去\(2^{tag_x}\)

然后加上\(\frac{1}{2},\frac{1}{4},...,\frac{1}{2^{tag_x}}=1-2^{tag_x}\)

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long

template<typename T>
inline void read(T&x){
    T k=1;char gc;x=0;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c))x=x*10+c-'0',gc;x*=k;
}

const int N=1e5+7;
const int p=998244353;
const ll Inv=(p+1)>>1;

int Pow[N];
int sum=0,prod=1;

inline int add(int a,int b){
    return (a+=b)>=p?a-p:a;
}

inline int sub(int a,int b){
    return (a-=b)<0?a+p:a;
}

int f[N<<2],g[N<<2],tag[N<<2];

#define ls (rt<<1)
#define rs (rt<<1|1)

inline void pushdown(int rt){
    if(tag[rt]){
        g[ls]=add((ll)g[ls]*Pow[tag[rt]]%p,sub(1,Pow[tag[rt]]));
        g[rs]=add((ll)g[rs]*Pow[tag[rt]]%p,sub(1,Pow[tag[rt]]));
        tag[ls]+=tag[rt];
        tag[rs]+=tag[rt];
        tag[rt]=0;
    }
}

void modify(int rt,int l,int r,int x,int y){
    sum=sub(sum,f[rt]);
    f[rt]=f[rt]*Inv%p;
    g[rt]=g[rt]*Inv%p;
    if(x<=l&&r<=y){
        f[rt]=add(f[rt],Inv);
        g[rt]=add(g[rt],Inv);
    }
    else if(l>y||r<x){
        f[rt]=add(f[rt],g[rt]);
        g[rt]=add(g[rt],g[rt]);
    }
    sum=add(sum,f[rt]);
    if(l>y||r<x)return ;
    if(x<=l&&r<=y){
        ++tag[rt];
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    modify(ls,l,mid,x,y);
    modify(rs,mid+1,r,x,y);
}

int main(){
    freopen("segment.in","r",stdin);
    freopen("segment.out","w",stdout);
    int n,m;r(n),r(m);
    Pow[0]=1;
    for(int i=1;i<=m;++i){
        Pow[i]=Pow[i-1]*Inv%p;
    }
    while(m--){
        int opt,l,r;r(opt);
        if(opt==1){
            r(l),r(r);
            modify(1,1,n,l,r);
            prod=add(prod,prod);
        }
        else{
            printf("%lld\n",(ll)sum*prod%p);
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM