[ZJOI2017] 树状数组

一、题目

点此看题

二、解法

解决这道题需要知道树状数组的原理, i i i 维护的是 ( i − l o w b i t ( i ) , i ] (i-lowbit(i),i] (ilowbit(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[l1,r1],所以 s u m [ r ] = s u m [ l − 1 ] sum[r]=sum[l-1] sum[r]=sum[l1] 就是回答正确的充要条件。那么我们维护这个式子成立的概率就行了。

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+(1p)(1q)一个个维护肯定不行,我们要看这个概率计算有没有什么性质能让我们加速处理,比如 结合律,如果有这个性质说不定会好推很多,我们设两个相邻的 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,L1],r[L,R],那么他变化的概率是 1 R − L + 1 \frac{1}{R-L+1} RL+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} RL+11
  • l , r ∈ [ l , R ] l,r\in[l,R] l,r[l,R],那么他变化的概率是 2 R − L + 1 \frac{2}{R-L+1} RL+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);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值