一、题目
二、解法
解决这道题需要知道树状数组的原理, i i i 维护的是 ( i − l o w b i t ( i ) , i ] (i-lowbit(i),i] (i−lowbit(i),i] 的求和。
那么如果反过来,那么 i i i 维护的是 [ i , i + l o w b i t ( i ) ) [i,i+lowbit(i)) [i,i+lowbit(i)) 的求和,所以原来求前缀和,现在求的是后缀和。
那么我们把正确的条件列出来: s u m [ l , r ] = s u m [ l − 1 , r − 1 ] sum[l,r]=sum[l-1,r-1] sum[l,r]=sum[l−1,r−1],所以 s u m [ r ] = s u m [ l − 1 ] sum[r]=sum[l-1] sum[r]=sum[l−1] 就是回答正确的充要条件。那么我们维护这个式子成立的概率就行了。
设
s
u
m
[
l
]
=
s
u
m
[
r
]
sum[l]=sum[r]
sum[l]=sum[r] 的概率为
p
p
p,在一次修改的时候某一个变化的概率为
q
q
q(只会有一个变化),那么我们就可以知道新的概率是:
p
q
+
(
1
−
p
)
(
1
−
q
)
pq+(1-p)(1-q)
pq+(1−p)(1−q)一个个维护肯定不行,我们要看这个概率计算有没有什么性质能让我们加速处理,比如 结合律,如果有这个性质说不定会好推很多,我们设两个相邻的
q
1
,
q
2
q1,q2
q1,q2,然后暴力展开式子,好,现在我们发现是相等的(省略过程,读者自己展开吧)
现在我们来考虑修改 [ L , R ] [L,R] [L,R] 对于每对 [ l , r ] [l,r] [l,r] 的影响,只有这三种:
- l ∈ [ 1 , L − 1 ] , r ∈ [ L , R ] l\in[1,L-1],r\in[L,R] l∈[1,L−1],r∈[L,R],那么他变化的概率是 1 R − L + 1 \frac{1}{R-L+1} R−L+11
- l ∈ [ L , R ] , r ∈ [ R + 1 , r ] l\in[L,R],r\in[R+1,r] l∈[L,R],r∈[R+1,r],那么他变化的概率是 1 R − L + 1 \frac{1}{R-L+1} R−L+11
- l , r ∈ [ l , R ] l,r\in[l,R] l,r∈[l,R],那么他变化的概率是 2 R − L + 1 \frac{2}{R-L+1} R−L+12
结合上面修改的区间性和更改概率的结合性,我们不难想到用数据结构来维护这个概率,这里我觉得选取标记永久化的线段树最好,因为要卡空间,下传标记会让空间大很多,所以干脆写标记永久化。
特殊地,注意到 f i n d ( x ) \tt find(x) find(x) 在 x = 0 x=0 x=0 时会直接返回 0 0 0,所以 l = 1 l=1 l=1 的时候要特殊处理,是 s u m [ 1 , r ] = s u m [ r , n ] sum[1,r]=sum[r,n] sum[1,r]=sum[r,n] 的概率,也是分上面三种情况讨论一下,在第一维线段树多开一个 0 0 0 点就行了。
#include <cstdio>
const int M = 100005;
const int MOD = 998244353;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,cnt,rt[4*M],tr[400*M],ls[400*M],rs[400*M];
int mul(int x,int y)
{
return (1ll*x*y+1ll*(1-x)*(1-y))%MOD;
}
void insy(int &x,int l,int r,int ql,int qr,int p)
{
if(ql>r || l>qr) return ;
if(!x) x=++cnt,tr[x]=1;
if(ql<=l && r<=qr)//错误1
{
tr[x]=mul(tr[x],p);
return ;
}
int mid=(l+r)>>1;
insy(ls[x],l,mid,ql,qr,p);
insy(rs[x],mid+1,r,ql,qr,p);
}
int asky(int x,int l,int r,int iy)
{
if(!x) return 1;
if(l==r) return tr[x];
int mid=(l+r)>>1;
if(mid>=iy) return mul(asky(ls[x],l,mid,iy),tr[x]);
return mul(asky(rs[x],mid+1,r,iy),tr[x]);
}
void insx(int i,int l,int r,int L,int R,int ql,int qr,int p)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
insy(rt[i],1,n,ql,qr,p);
return ;
}
int mid=(l+r)>>1;
insx(i<<1,l,mid,L,R,ql,qr,p);
insx(i<<1|1,mid+1,r,L,R,ql,qr,p);
}
int askx(int i,int l,int r,int ix,int iy)
{
if(l==r) return asky(rt[i],1,n,iy);
int mid=(l+r)>>1,t=asky(rt[i],1,n,iy);
if(ix<=mid) return mul(t,askx(i<<1,l,mid,ix,iy));//错误2
return mul(t,askx(i<<1|1,mid+1,r,ix,iy));
}
int qkpow(int a,int b)
{
int r=1;
while(b>0)
{
if(b&1) r=1ll*r*a%MOD;
a=1ll*a*a%MOD;
b>>=1;
}
return r;
}
signed main()
{
n=read();m=read();
while(m--)
{
int op=read(),l=read(),r=read();
if(op==1)
{
int p=qkpow(r-l+1,MOD-2);
if(l>1) insx(1,0,n,1,l-1,l,r,1-p),insx(1,0,n,0,0,1,l-1,0);
if(r<n) insx(1,0,n,l,r,r+1,n,1-p),insx(1,0,n,0,0,r+1,n,0);
insx(1,0,n,l,r,l,r,(1-2*p+MOD)%MOD),insx(1,0,n,0,0,l,r,p);
}
else
printf("%d\n",(askx(1,0,n,l-1,r)+MOD)%MOD);
}
}