【ZJOI2019】【LOJ3043】【洛谷P5280】线段树

洛谷传送门

LOJ传送门


解析:

我们改变一下修改和回答方式。

令每个修改操作的被执行概率是 1 2 \frac{1}{2} 21,然后我们直接询问这棵线段树中被染黑的点数的期望,乘上 2 t 2^t 2t就行了, t t t是修改操作的次数。

f u , 0 f_{u,0} fu,0表示 u u u点被染黑的概率, f u , 1 f_{u,1} fu,1表示 u u u没黑但是 u u u存在某一个祖先黑了的概率, f u , 2 f_{u,2} fu,2表示 u u u没黑且不存在任何一个祖先黑了的概率。

显然初始化为 f u , 2 = 1 f_{u,2}=1 fu,2=1

每次修改中,考虑 5 5 5类点:

1.访问但不定位
2.访问且定位
3.在 2 2 2类点的子树中
4.父亲为 1 1 1类点
5.这次修改管它屁事的点。

显然 5 5 5类点的所有概率没有任何变动。考虑前四种的变化:

1,访问但不定位 ( f 0 f 1 f 2 ) ⟹ ( 1 2 f 0 1 2 f 1 1 2 f 0 + 1 2 f 1 + f 2 ) \begin{pmatrix}f_0\\f_1\\f_2\end{pmatrix}\Longrightarrow\begin{pmatrix}\frac{1}2f_0\\\frac{1}{2}f_1\\\frac{1}{2}f_0+\frac{1}{2}f_1+f_2\end{pmatrix} f0f1f221f021f121f0+21f1+f2

2,访问且被定位
( f 0 f 1 f 2 ) ⟹ ( f 0 + 1 2 f 1 + 1 2 f 2 1 2 f 1 1 2 f 2 ) \begin{pmatrix}f_0\\f_1\\f_2\end{pmatrix}\Longrightarrow\begin{pmatrix}f_0+\frac{1}2f_1+\frac{1}2f_2\\\frac{1}2f_1\\\frac{1}2f_2\end{pmatrix} f0f1f2f0+21f1+21f221f121f2

3,在 2 2 2类点的子树中

( f 0 f 1 f 2 ) ⟹ ( f 0 f 1 + 1 2 f 2 1 2 f 2 ) \begin{pmatrix}f_0\\f_1\\f_2\end{pmatrix}\Longrightarrow\begin{pmatrix}f_0\\f_1+\frac{1}{2}f_2\\\frac{1}{2}f_2\end{pmatrix} f0f1f2f0f1+21f221f2

特别的,我们需要为这类点维护懒标记,来计算它被多少次算在了 2 2 2的子树中。

4,父亲为 1 1 1类点
( f 0 f 1 f 2 ) ⟹ ( f 0 + 1 2 f 1 1 2 f 1 f 2 ) \begin{pmatrix}f_0\\f_1\\f_2\end{pmatrix}\Longrightarrow\begin{pmatrix}f_0+\frac{1}2f_1\\\frac{1}2f_1\\f_2\end{pmatrix} f0f1f2f0+21f121f1f2


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

using std::cout;
using std::cerr;

cs int mod=998244353,inv2=(mod+1)/2;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Inc(int &a,int b){(a+=b)>=mod?a-=mod:a;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b>=mod?(ll)a*b%mod:(ll)(a*b);}
inline int half(int a){return (a&1)?(a+mod)>>1:(a>>1);}

cs int N=1e5+5;

int i2[N];
inline void init(int lim){
	i2[0]=1;
	for(int re i=1;i<=lim;++i)i2[i]=mul(inv2,i2[i-1]);
}

struct vec{
	int a[3];
	vec(){}
	
	int &operator[](int offset){return a[offset];}
	cs int &operator[](int offset)cs{return a[offset];}
	
	void push1(){
		a[0]=half(a[0]);
		a[1]=half(a[1]);
		Inc(a[2],add(a[0],a[1]));
	}
	void push2(){
		a[1]=half(a[1]);
		a[2]=half(a[2]);
		Inc(a[0],add(a[1],a[2]));
	}
	void push3(int p){
		int tmp=mul(i2[p],a[2]);
		Inc(a[1],dec(a[2],tmp));
		a[2]=tmp; 
	}
	void push4(){
		Inc(a[0],half(a[1]));
		a[1]=half(a[1]);
	}
};

vec mat[N<<2];
int tag[N<<2],sum[N<<2];
cs int lim=N<<2;

inline void pushup(int k){
	sum[k]=mat[k][0];
	if((k<<1)<lim)Inc(sum[k],add(sum[k<<1],sum[k<<1|1]));
}

inline void build(int k,int l,int r){
	mat[k][2]=1;
	if(l==r)return ;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

inline void push3(int k,int p){
	tag[k]+=p;
	mat[k].push3(p);
	pushup(k);
}
inline void push4(int k){
	mat[k].push4();
	pushup(k);
}

inline void pushdown(int k){
	if(tag[k]){
		if((k<<1)<lim)
		push3(k<<1,tag[k]),push3(k<<1|1,tag[k]);
		tag[k]=0;
	}
}

inline void modify(int k,int l,int r,cs int &ql,cs int &qr){
	pushdown(k);
	if(ql<=l&&r<=qr){
		mat[k].push2();
		if(l^r){
			push3(k<<1,1);
			push3(k<<1|1,1);
		}
		pushup(k);
		return ;
	}
	int mid=(l+r)>>1;
	mat[k].push1();
	if(qr<=mid){
		push4(k<<1|1);
		modify(k<<1,l,mid,ql,qr);
	}
	else if(mid<ql){
		push4(k<<1);
		modify(k<<1|1,mid+1,r,ql,qr);
	}
	else {
		modify(k<<1,l,mid,ql,qr);
		modify(k<<1|1,mid+1,r,ql,qr);
	}
	pushup(k);
}

int n,m;
signed main(){
	n=getint(),m=getint();
	init(m);
	build(1,1,n);
	int now=1;
	while(m--)switch(getint()){
		case 1:{
			int l=getint(),r=getint();
			modify(1,1,n,l,r);
			Inc(now,now);
			break;
		}
		case 2:{
			cout<<mul(now,sum[1])<<"\n";
			break;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值