【洛谷3688】[ZJOI2017] 树状数组(二维线段树)

点此看题面

  • 给定一个长度为\(n\)的数组,有两种操作:
    • 随机给区间\([l,r]\)中某一位置的值加上\(1\)
    • 询问区间\([l,r]\)所有位置的和模\(2\)的余数。
  • 对于每个询问,求如果把树状数组写反了,与正确的答案相同的概率。
  • \(n\le10^5\)

感觉这道题应该不是很难吧,至少我都能自己做出来。

不过这可能也是得益于不小心看到了二维线段树这一标签。。。

题意转化

注意这里的询问是以\(2\)为模数的,而\(x\equiv -x(mod\ 2)\)

发现树状数组写反了,就意味着现在得到的是区间\([l-1,r-1]\)所有位置的和。

那么也就是求\(a_{l-1}\)\(a_r\)相同的概率。

二维线段树

考虑我们用\((i,j)\)表示\(i\)\(j\)两个位置值相同的概率。

对于一次修改\([l,r]\),令\(t=\frac{1}{r-l+1}\),然后发现对于\((i,j)\)有以下几种变化的可能:

  • \(i,j\)都不在\([l,r]\)范围内,无影响。
  • \(i,j\)都在\([l,r]\)范围内,有\(2t\)的概率选中其中一个数导致值改变,\(1-2t\)的概率没选中。
  • \(i,j\)有且仅有一个在\([l,r]\)范围内,有\(t\)的概率选择区间中那个数导致值改变,\(1-t\)的概率没选中。

考虑如何合并两个值不变的概率\(x,y\),容易发现就是\(x\times y+(1-x)\times(1-y)\)。(即要么都不变,要么都变)

那么就可以直接拿二维线段树维护了。

具体实现时为了好写,可以考虑标记永久化

\(l=1\)的特殊情况

注意,其实我们还要特殊处理\(l=1\),即\(l-1=0\)的情况。

此时得到的应该是\([r,n]\)所有位置的和,而本应得到的是\([1,r]\)所有位置的和,也就是要求出除第\(r\)个位置以外所有数之和为\(0\)的概率。

可以发现一个位置与\(0\)的答案值不变的概率就是它作为被修改位置的概率。

这其实和一般情况还是基本一致的,只要单独对这种情况开一个\(0\)号节点就好了,省得重写一个线段树。

代码:\(O(nlog^2n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LN 20
#define X 998244353
using namespace std;
int n;I int G(CI x,CI y) {return (1LL*x*y+1LL*(1-x+X)*(1-y+X))%X;}//合并两个值不变的概率
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class SegSegTree//标记永久化二维线段树
{
	private:
		#define PT CI l=0,CI r=n
		#define LT l,mid
		#define RT mid+1,r
		int Rt[N<<2];class SegmentTree
		{
			private:
				int Nt;struct node{int V,S[2];}O[N*LN*LN+5];
			public:
				I void U(int& rt,CI L,CI R,CI v,PT)//区间修改
				{
					if(!rt&&(O[rt=++Nt].V=1),L<=l&&r<=R) return (void)(O[rt].V=G(O[rt].V,v));
					RI mid=l+r>>1;L<=mid&&(U(O[rt].S[0],L,R,v,LT),0),R>mid&&(U(O[rt].S[1],L,R,v,RT),0);
				}
				I int Q(CI rt,CI y,PT)//单点询问
				{
					if(!rt) return 1;if(l==r) return O[rt].V;RI mid=l+r>>1;
					return G(y<=mid?Q(O[rt].S[0],y,LT):Q(O[rt].S[1],y,RT),O[rt].V);
				}
		}S;
	public:
		I void U(CI L,CI R,CI _l,CI _r,CI v,PT,CI rt=1)//区间修改
		{
			if(L<=l&&r<=R) return S.U(Rt[rt],_l,_r,v);RI mid=l+r>>1;
			L<=mid&&(U(L,R,_l,_r,v,LT,rt<<1),0),R>mid&&(U(L,R,_l,_r,v,RT,rt<<1|1),0);
		}
		I int Q(CI x,CI y,PT,CI rt=1)//单点询问
		{
			if(l==r) return S.Q(Rt[rt],y);RI mid=l+r>>1;
			return G(x<=mid?Q(x,y,LT,rt<<1):Q(x,y,RT,rt<<1|1),S.Q(Rt[rt],y));
		}
}T;
int main()
{
	RI Qt,op,x,y,t;scanf("%d%d",&n,&Qt);W(Qt--) switch(scanf("%d%d%d",&op,&x,&y),op)
	{
		case 1:t=QP(y-x+1,X-2),T.U(x,y,x,y,(1-2LL*t%X+X)%X),T.U(0,0,x,y,t),//两个都在区间中
			x^1&&(T.U(1,x-1,x,y,(1-t+X)%X),T.U(0,0,1,x-1,0),0),//有且仅有一个
			y^n&&(T.U(x,y,y+1,n,(1-t+X)%X),T.U(0,0,y+1,n,0),0);break;//有且仅有一个
		case 2:printf("%d\n",T.Q(x-1,y));break;//查询x-1和y值相同的概率
	}return 0;
}
posted @ 2020-12-14 21:05  TheLostWeak  阅读(35)  评论(0编辑  收藏