树状数组

首先我们发现如果reverse下树状数组所操作的数组的话,它写的代码是完全正确的,但是reverse之后前缀变成了后缀,所以,它写了一个正确的单点修改求后缀和的数据结构

那么我们发现,对于一个通常的询问,其实是在询问(l-1,r)这两个点相同的概率

,然后一个错误的思路是用线段树维护每一个点被修改的概率,但是这样做是错的,因为对于一次操作,修改且只修改一个点,如果按照线段树来做,其实是会导致一次操作修改多个点的

那怎么办呢,二维线段树好了,因为它可以同时维护两维的信息

设二元组(x,y)的值表示位置为x的点和位置为y的点相同的概率

那么我们发现,对于一个操作(l,r),设这次操作的区间长度为len

他会对三种类型的二元组产生影响

1.左端点在(l,r)内且右端点不在(l,r)的二元组

2.右端点在(l,r)内且左端点不在(l,r)的二元组

3.左右端点同时在(l,r)内的二元组

对于第一种和第二种二元组,这次修改有1/len的概率取反它们,对于第3种二元组这次修改有2/len的概率取反它们(因为不存在同时落在x和y上的操作,每次操作之后修改一个点)

发现l==1的时候询问的是前缀和是否等于后缀和

单开一个线段树记录即可,注意特判

上代码~

// luogu-judger-enable-o2
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<cstdio>
#define ls ch[o][0]
#define rs ch[o][1]
#define mid (l+r>>1) 
const int M=101000,mod=998244353;int n,m,l,r,opt;
int merge(int x,int y){return (1ll*x*y%mod+1ll*(1ll-x+mod)%mod*(1ll-y+mod)%mod)%mod;}
int pow(int a,int k){int ans=1;for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) ans=1ll*ans*a%mod;return ans;}
struct SEG{int root[M<<8];
    struct SEG2{
        int ch[M<<8][2],tot,p[M<<8];
        void modify(int &o,int l,int r,int ql,int qr,int P){
            if(r<ql||l>qr) return ;if(!o) o=++tot,p[o]=1;
            if(ql<=l&&r<=qr) {p[o]=merge(p[o],P);return ;}
            modify(ls,l,mid,ql,qr,P);modify(rs,mid+1,r,ql,qr,P);
        }
        int query(int o,int l,int r,int ins){
            if(!o)return 1; if(l==r) return p[o];
            if(ins<=mid) return merge(p[o],query(ls,l,mid,ins));
            else return merge(p[o],query(rs,mid+1,r,ins));
        }
    }T;
    void modify(int o,int l,int r,int ql,int qr,int ql2,int qr2,int p){
        if(r<ql||l>qr) return ;
        if(ql<=l&&r<=qr) {T.modify(root[o],0,n,ql2,qr2,p);return ;}
        modify(o<<1,l,mid,ql,qr,ql2,qr2,p);modify(o<<1|1,mid+1,r,ql,qr,ql2,qr2,p);
    }
    int query(int o,int l,int r,int ql,int qr){
        if(l==r) return T.query(root[o],0,n,qr);
        if(ql<=mid) return merge(T.query(root[o],0,n,qr),query(o<<1,l,mid,ql,qr));
        else return merge(T.query(root[o],0,n,qr),query(o<<1|1,mid+1,r,ql,qr));
    }

}T;
int main(){
    scanf("%d%d",&n,&m);
    while(m--){scanf("%d%d%d",&opt,&l,&r);
        if(opt==1) {int P=pow(r-l+1,mod-2);
            if(l>1) T.modify(1,0,n,1,l-1,l,r,(1-P+mod)%mod),T.modify(1,0,n,0,0,0,l-1,0);
            if(r<n) T.modify(1,0,n,l,r,r+1,n,(1-P+mod)%mod),T.modify(1,0,n,0,0,r+1,n,0);
            T.modify(1,0,n,l,r,l,r,(1-2*P%mod+mod)%mod);
            T.modify(1,0,n,0,0,l,r,P);
        }else printf("%d\n",T.query(1,0,n,l-1,r));
    }   
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值