BZOJ4785: [Zjoi2017]树状数组

10 篇文章 0 订阅
9 篇文章 0 订阅

题目大意:给了一种错误的实现树状数组方式,每次随机在一个区间里修改一个值,问每次询问得到的答案是正确的概率

首先我们可以慢慢琢磨一下这份代码:发现其实他就是在求后缀和
然后由于模2,其实整道题都相当于在异或
这样每次 Query(L,R)=Find(R)Find(L1)=Xor(R,n)Xor(L1,n)=Xor(L1,R1)
而实际上我们要求的是 Xor(L,R) ,所以区别就在于L-1和R这两个位置
我们只需要统计:在这次询问之前的修改,有多少概率修改了这两个点总共偶数次

我们可以对之前的修改操作建一个树套树,其中对左端点建外层线段树,对右端点建内层平衡树,这样就可以支持我们查询左端点在一定范围内,右端点在另一个范围内,修改次数为偶数次的概率是多少了

需要注意:1.我们需要维护两个概率,p和2*p,分别代表L-1和R在这个区间内出现一次和两次的概率,然后分类讨论分别计算
2.当询问的 L=1 时,实际上 Query(L,R)=Xor(R,n)=Xor(1,n)Xor(1,R1) ,所以这个时候答案只和R被修改偶数次的概率和总修改次数有关

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define M 2000010
using namespace std;
int mod=998244353;
int l[N<<2],r[N<<2],rt[N<<2];
int ls[M],rs[M],rnd[M],a[M],LL[M],RR[M],cnt;
long long a1[M],a2[M],A1[M],A2[M];
void build(int now,int ll,int rr)
{
    l[now]=ll;r[now]=rr;
    if(ll==rr) return;
    int mid=(ll+rr)>>1;
    build(now<<1,ll,mid);
    build(now<<1|1,mid+1,rr);
}
void pup(int x)
{
    a1[x]=(a1[ls[x]]*a1[rs[x]]+(mod+1-a1[ls[x]])*(mod+1-a1[rs[x]]))%mod;
    a2[x]=(a2[ls[x]]*a2[rs[x]]+(mod+1-a2[ls[x]])*(mod+1-a2[rs[x]]))%mod;
    int tmp1,tmp2;
    a1[x]=(a1[x]*A1[x]+(mod+1-a1[x])*(mod+1-A1[x]))%mod;
    a2[x]=(a2[x]*A2[x]+(mod+1-a2[x])*(mod+1-A2[x]))%mod;
    if(ls[x]) LL[x]=LL[ls[x]];else LL[x]=a[x];
    if(rs[x]) RR[x]=RR[rs[x]];else RR[x]=a[x];
}
void zxu(int &k)
{
    int t=ls[k];
    ls[k]=rs[t];
    rs[t]=k;
    pup(k);pup(t);
    k=t;
}
void yxu(int &k)
{
    int t=rs[k];
    rs[k]=ls[t];
    ls[t]=k;
    pup(k);pup(t);
    k=t;
}
void add(int &k,int x,int v)
{
    if(!k)
    {
        cnt++;k=cnt;
        A1[cnt]=a1[cnt]=(1-v+mod)%mod;
        A2[cnt]=a2[cnt]=(1-2*v%mod+mod)%mod;
        rnd[cnt]=rand();LL[cnt]=RR[cnt]=a[cnt]=x;
        return;
    }
    if(a[k]==x)
    {
        int tmp1,tmp2;
        a1[k]=(a1[k]*(1-v+mod)+(mod+1-a1[k])*v)%mod;
        a2[k]=(a2[k]*(1-2*v%mod+mod)+(mod+1-a2[k])*2*v)%mod;

        A1[k]=(A1[k]*(1-v+mod)+(mod+1-A1[k])*v)%mod;
        A2[k]=(A2[k]*(1-2*v%mod+mod)+(mod+1-A2[k])*2*v)%mod;
        return;
    }
    if(a[k]<x)
    {
        add(rs[k],x,v);
        if(rnd[rs[k]]>rnd[k]) yxu(k);
    }
    else
    {
        add(ls[k],x,v);
        if(rnd[ls[k]]>rnd[k]) zxu(k);
    }
    pup(k);
}
void change(int now,int x,int y,int v)
{
    add(rt[now],y,v);
    if(l[now]==r[now]) return;
    int mid=(l[now]+r[now])>>1;
    if(x<=mid) change(now<<1,x,y,v);
    else change(now<<1|1,x,y,v);
}
long long ansa;
int L,R;
void query1(int k)
{
    if(!k) return;
    if(LL[k]>R||RR[k]<L) return;
    if(LL[k]>=L&&RR[k]<=R)
    {
        ansa=(ansa*a1[k]+(mod+1-ansa)*(mod+1-a1[k]))%mod;
        return;
    }
    if(R<a[k]) query1(ls[k]);
    else if(L>a[k]) query1(rs[k]);
    else
    {
        ansa=(ansa*A1[k]+(mod+1-ansa)*(mod+1-A1[k]))%mod;
        query1(ls[k]);
        query1(rs[k]);
    }
}
void query2(int k)
{
    if(!k) return;
    if(LL[k]>R||RR[k]<L) return;
    if(LL[k]>=L&&RR[k]<=R)
    {
        ansa=(ansa*a2[k]+(mod+1-ansa)*(mod+1-a2[k]))%mod;
        return;
    }
    if(R<a[k]) query2(ls[k]);
    else if(L>a[k]) query2(rs[k]);
    else
    {
        ansa=(ansa*A2[k]+(mod+1-ansa)*(mod+1-A2[k]))%mod;
        query2(ls[k]);
        query2(rs[k]);
    }
}
void check1(int now,int ll,int rr)
{
    if(l[now]==ll&&r[now]==rr)
    {
        query1(rt[now]);
        return;
    }
    int mid=(l[now]+r[now])>>1;
    if(rr<=mid) check1(now<<1,ll,rr);
    else if(ll>mid) check1(now<<1|1,ll,rr);
    else check1(now<<1,ll,mid),check1(now<<1|1,mid+1,rr);
}
void check2(int now,int ll,int rr)
{
    if(l[now]==ll&&r[now]==rr)
    {
        query2(rt[now]);
        return;
    }
    int mid=(l[now]+r[now])>>1;
    if(rr<=mid) check2(now<<1,ll,rr);
    else if(ll>mid) check2(now<<1|1,ll,rr);
    else check2(now<<1,ll,mid),check2(now<<1|1,mid+1,rr);
}
int ksm(int d,int c)
{
    long long ret=1;
    while(c)
    {
        if(c&1) ret=ret*d%mod;
        d=(long long)d*d%mod;c/=2;
    }
    return (int)ret;
}
char __ch,__B[1<<15],*__S=__B,*__T=__B;
#define getc() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?0:*__S++)
#define isd(c) (c>='0'&&c<='9')
int __a;
int read(){
    while(__ch=getc(),!isd(__ch));(__a=__ch-'0');
    while(__ch=getc(),isd(__ch))__a=__a*10+__ch-'0';return __a;
}
int main()
{
    int n,m;
    n=read();m=read();
    int o,x,y;
    build(1,1,n);
    a1[0]=a2[0]=1;
    int totchange=0;
    int tot=0;
    while(m--)
    {
        o=read();x=read();y=read();
        if(o==1) totchange++,change(1,x,y,ksm(y-x+1,mod-2));
        else
        {
            if(x!=1)
            {
                x--;

                ansa=1;
                L=x;R=y-1;
                check1(1,1,x);
                L=y;R=n;
                check2(1,1,x);
                L=y;R=n;
                check1(1,x+1,y);
            }
            else
            {
                ansa=1;
                L=y;R=n;
                check1(1,1,y);
                if(totchange&1) ansa=mod+1-ansa;
            }
            printf("%lld\n",ansa%mod);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值