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

点此看题面

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

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

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

题意转化

注意这里的询问是以(2)为模数的,而(xequiv -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 imes y+(1-x) imes(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
",T.Q(x-1,y));break;//查询x-1和y值相同的概率
	}return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3688.html