题目大意:给了一种错误的实现树状数组方式,每次随机在一个区间里修改一个值,问每次询问得到的答案是正确的概率
首先我们可以慢慢琢磨一下这份代码:发现其实他就是在求后缀和
然后由于模2,其实整道题都相当于在异或
这样每次
Query(L,R)=Find(R)−Find(L−1)=Xor(R,n)⊕Xor(L−1,n)=Xor(L−1,R−1)
而实际上我们要求的是
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,R−1)
,所以这个时候答案只和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);
}
}
}