【LOJ#3043】【洛谷P5280】【ZJOI2019】—线段树(计数dp+线段树)

本文介绍了一种结合期望和数据结构的算法题解决思路,通过分析复制操作,利用线段树来维护每种情况的可能性,进而求解复杂的数据结构问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LOJ传送门

洛谷传送门

早上在知乎看到吉司机说这是一道期望+数据结构
一脸懵逼

原来就是把计数看成期望乘上情况


分析一波复制操作
就可以发现其实就是求每次操作有 / / /没有的 2 t 2^t 2t种情况的 t a g tag tag之和

如果按照线段树这个样子
似乎……可以直接用线段树维护?
维护一个 t r [ u ] tr[u] tr[u]表示线段树上点 u u u当前有多少种情况为 1 1 1

没有影响到的点显然 t a g tag tag是不会改变的的,也就是 t r tr tr乘2

对于所有线段树遍历而且非修改的点,他们的 t a g tag tag显然都被变成 0 0 0
所以这些点 t r tr tr不变

对于所有直接修改到的节点显然无论前面情况怎么样现在的 t a g tag tag都为1
也就是加上 2 t 2^t 2t

发现 p u s h d o w n pushdown pushdown还会影响所有修改节点的兄弟
但是只有当原来父亲以上有 t a g tag tag为1的时候这里才会变成1
再维护一个 f [ u ] f[u] f[u]表示有多少种情况 f [ u ] f[u] f[u]到根会有点 t a g tag tag为1
t r tr tr直接加上 f f f就是了

那考虑 f f f怎么维护
对于不被修改的点, t a g tag tag不变, f f f乘2

修改路径上的点, t a g tag tag变成了0, f f f不变

被修改的点, t a g tag tag变成了 1 1 1 f f f加上 2 t 2^t 2t

所有兄弟, t a g tag tag变成了 1 1 1 f f f乘2

又发现所有不被修改的点都是兄弟的儿子
所以就只用讨论3种情况了

修改路径上的点: t r tr tr不变, f f f不变

被修改的点: t r + = 2 t tr+=2^t tr+=2t,子树所有 f + = 2 t f+=2^t f+=2t

兄弟节点:子树所有 t r + = f tr+=f tr+=f,子树所有 f = f ∗ 2 f=f*2 f=f2

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0,f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
const int N=100005;
const int mod=998244353;
inline int add(int a,int b){
	return a+b>=mod?a+b-mod:a+b;
}
inline int mul(int a,int b){
	return 1ll*a*b>=mod?1ll*a*b%mod:a*b;
}
inline void dec(int &a,int b){
	a=a>=b?a-b:a-b+mod;
}
inline void selfadd(int &a,int b){
	a=add(a,b);
}
inline void selfmul(int &a,int b){
	a=mul(a,b);
}
int ans,t,n,m,res;
namespace Seg{
	int tr[N<<2],f[N<<2],mulf[N<<2],addf[N<<2],mul[N<<2];
	#define lc (u<<1)
	#define rc ((u<<1)|1)
	#define mid ((l+r)>>1)
	inline void build(int u,int l,int r){
		mulf[u]=mul[u]=1;
		if(l==r)return;
		build(lc,l,mid),build(rc,mid+1,r);
	}
	inline void pushmulf(int u,int k){
		selfmul(addf[u],k),selfmul(f[u],k),selfmul(mulf[u],k);
	}
	inline void pushadd(int u,int k){
		selfadd(f[u],k),selfadd(addf[u],k);
	}
	inline void pushmul(int u,int k){
		selfmul(mul[u],k),selfmul(tr[u],k);
	}
	inline void pushdown(int u){
		if(mulf[u])pushmulf(lc,mulf[u]),pushmulf(rc,mulf[u]),mulf[u]=1;
		if(addf[u])pushadd(lc,addf[u]),pushadd(rc,addf[u]),addf[u]=0;
		if(mul[u])pushmul(lc,mul[u]),pushmul(rc,mul[u]),mul[u]=1;
	}
	void update(int u,int l,int r,int st,int des){
		if(st<=l&&r<=des){
			dec(ans,tr[u]);
			selfadd(tr[u],t),pushadd(u,t),selfmul(mul[u],2);
			selfadd(res,tr[u]);
			return;
		}
		if(r<st||des<l){
			dec(ans,tr[u]);
			selfadd(tr[u],f[u]),selfmul(mul[u],2),pushmulf(u,2);
			selfadd(res,tr[u]);
			return;
		}
		pushdown(u);
		dec(ans,tr[u]),selfadd(res,tr[u]);
		update(lc,l,mid,st,des);
		update(rc,mid+1,r,st,des);
	}
}
using namespace Seg;
int main(){
	n=read(),m=read();
	build(1,1,n);t=1;
	for(int i=1;i<=m;i++){
		int op=read();
		if(op==1){
			int l=read(),r=read();
			res=0;
			update(1,1,n,l,r);
			selfmul(t,2);
			selfmul(ans,2);
			selfadd(ans,res);
		}
		else cout<<ans<<'\n';
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值