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

LOJ传送门

洛谷传送门

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

#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';
	}
}

猜你喜欢

转载自blog.csdn.net/qq_42555009/article/details/89056502