[ZJOI2019]线段树(线段树)

看到这题,首先想到将求和转期望,即每次操作进行概率为1/2,求节点打标记概率。

首先对于每次区间修改操作,对节点进行分类:

1、这个点和其父亲都和修改区间无交,这种情况可以无视。

2、这个点和修改区间无交但父亲和修改区间有交,这样该区间有无标记只和本身及是否存在一个祖先有标记相关。

3、这个点被修改区间覆盖,且父节点也被覆盖,则无变化。

4、这个点和修改区间有交但没有被完全包含,则不会有标记(因为要pushdown)。

然后记录该节点有标记的概率f,和祖先至少有一个有标记的概率g,然后根据上面表述的意思转移即可。

#include<bits/stdc++.h>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=2e5+7,mod=998244353,inv2=499122177;
int n,m,pw[N],inv[N],f[N<<2],g[N<<2],s[N<<2],tag[N<<2];
void pushup(int rt){s[rt]=(1ll*s[rt<<1]+s[rt<<1|1]+f[rt])%mod;}
void modify(int rt,int v){g[rt]=1ll*(g[rt]+pw[v]-1)*inv[v]%mod,tag[rt]+=v;}
void pushdown(int rt)
{
    if(!tag[rt])return;
    modify(rt<<1,tag[rt]),modify(rt<<1|1,tag[rt]);
    tag[rt]=0;
}
void update(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        tag[rt]++,f[rt]=1ll*(f[rt]+1)*inv2%mod,g[rt]=1ll*(g[rt]+1)*inv2%mod;
        pushup(rt);return;
    }
    if(L>r||R<l)
    {
        f[rt]=1ll*(f[rt]+g[rt])*inv2%mod;
        pushup(rt);return;
    }
    pushdown(rt);
    int mid=l+r>>1;
    f[rt]=1ll*f[rt]*inv2%mod,g[rt]=1ll*g[rt]*inv2%mod;
    update(L,R,lson),update(L,R,rson);
    pushup(rt);
}
int main()
{
    scanf("%d%d",&n,&m);
    pw[0]=inv[0]=1;for(int i=1;i<=n;i++)pw[i]=2ll*pw[i-1]%mod,inv[i]=1ll*inv[i-1]*inv2%mod;
    int num=1;
    while(m--)
    {
        int op,l,r;scanf("%d",&op);
        if(op==2)printf("%d\n",1ll*s[1]*num%mod);
        else scanf("%d%d",&l,&r),num=2ll*num%mod,update(l,r,1,n,1);
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/hfctf0210/p/10791669.html