P3688 [ZJOI2017] 树状数组 题解 / 二维线段树 + 概率论

题目传送门:P3688 [ZJOI2017] 树状数组

容易发现她求的其实是后缀和,因此只需要计算 \(a_{l-1}=a_r\) 的概率。

假设 \(p_{x,y}\) 表示 \(x,y\) 相同的概率,那么对于一个区间 \([l,r]\)

  1. \(x,y\in [l,r]\) 那么 \(x,y\)\(\frac{2}{r-l+1}\) 概率不相同。
  2. \(x \in [1,l],y\in [l,r]\) 那么 \(x,y\)\(\frac{1}{r-l+1}\) 概率不相同。
  3. \(x \in (l,r],y\in [r,n]\) 那么 \(x,y\)\(\frac{1}{r-l+1}\) 概率不相同。

然后发现对于一个事件发生的概率为 \(p\),有 \(q\) 的概率使其不取反,那么新概率即为 \(pq+(1-p)(1-q)\),然后使用二维线段树维护一下 \(p_{x,y}\) 即可。

有一个特殊情况当 \(l=1\) 是判断是 \(\sum_{i=1}^r a_r = \sum_{i=r}^n a_r\),与上面情况类似。

#include<bits/stdc++.h>
#define double long double
using namespace std;
const int N=1e5+1,mod=998244353; 
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
#define lc ls[k]
#define rc rs[k]
int ls[N*350],rs[N*350],add[N*350],root[N<<2],cnt,n,m;
inline int get(int x,int y){return (1ll*x*y%mod+1ll*(1-x+mod)*(1-y+mod)%mod)%mod;}
void exgcd(int a,int b,int &x,int &y){
	if (b==0) x=1,y=0;
	else exgcd(b,a%b,y,x),y-=a/b*x;
}
inline int inv(int a){
	int x,y;
	exgcd(a,mod,x,y);
	return (x%mod+mod)%mod;
}
void change(int &k,int l,int r,int l1,int r1,int x){
	if (l1>r1) return ;
	if (!k) k=++cnt,add[k]=1;
	if (l1<=l&&r1>=r){add[k]=get(add[k],x);return ;}
	int mid=l+r>>1; 
	if (l1<=mid) change(lc,l,mid,l1,r1,x);
	if (r1>mid) change(rc,mid+1,r,l1,r1,x);
}
int ask(int k,int l,int r,int x){
	if (!k) return 1;
	if (l==r) return add[k];
	int mid=l+r>>1;
	if (x<=mid) return get(add[k],ask(lc,l,mid,x));
	else return get(add[k],ask(rc,mid+1,r,x));
}
#undef lc 
#undef rc
#define lc k<<1
#define rc k<<1|1
void change(int k,int l,int r,int l1,int r1,int l2,int r2,int x){
	if (l1>r1) return ;
	if (l1<=l&&r1>=r){change(root[k],1,n,l2,r2,x);return ;}
	int mid=l+r>>1;
	if (l1<=mid) change(lc,l,mid,l1,r1,l2,r2,x);
	if (r1>mid) change(rc,mid+1,r,l1,r1,l2,r2,x);
}
int ask(int k,int l,int r,int x,int y){
	if (l==r) return ask(root[k],1,n,y); 
	int mid=l+r>>1;
	int tmp=ask(root[k],1,n,y);
	if (x<=mid) return get(tmp,ask(lc,l,mid,x,y));
	else return get(tmp,ask(rc,mid+1,r,x,y));
}
#undef lc 
#undef rc
#define lc ls[k]
#define rc rs[k]
int idx;
struct tree{
	int add[N<<2],ls[N<<2],rs[N<<2];
	void change(int &k,int l,int r,int l1,int r1,int x){
		if (l1>r1) return ;
		if (!k) k=++idx,add[k]=1;
		if (l1<=l&&r1>=r){add[k]=get(add[k],x);return ;}
		int mid=l+r>>1; 
		if (l1<=mid) change(lc,l,mid,l1,r1,x);
		if (r1>mid) change(rc,mid+1,r,l1,r1,x);
	}
	int ask(int k,int l,int r,int x){
		if (!k) return 1;
		if (l==r) return add[k];
		int mid=l+r>>1;
		if (x<=mid) return get(add[k],ask(lc,l,mid,x));
		else return get(add[k],ask(rc,mid+1,r,x));
	}	 
}tree;
main(){
	n=read(),m=read();
	int root;
	while(m--){
		int op=read(),l=read(),r=read();
		if (op==1){
			int tmp=inv(r-l+1);
			change(1,1,n,l,r,l,r,1ll*(r-l-1+mod)*tmp%mod);
			change(1,1,n,1,l-1,l,r,1ll*(r-l)*tmp%mod);
			change(1,1,n,l,r,r+1,n,1ll*(r-l)*tmp%mod); 
			tree.change(root,1,n,l,r,tmp);
			tree.change(root,1,n,1,l-1,0);
			tree.change(root,1,n,r+1,n,0);
		}
		else{
			int tmp;
			if (l==1) tmp=tree.ask(root,1,n,r);
			else tmp=ask(1,1,n,l-1,r);
			printf("%d\n",tmp%mod); 
		}
	}
    return 0;
}
posted @ 2026-01-21 16:35  OTn53_qwq  阅读(13)  评论(0)    收藏  举报