【线段树】LOJ3043 [ZJOI2019] 线段树

【前言】
这个题想的时候想错了几次,后面看了别人的blog才发现偏差。

【题目】
LOJ
初始有一棵线段树,没有标记,对于一次操作 [ l , r ] [l,r] [l,r],将所有线段树复制一份,对于奇数标号的所有线段树,我们像普通线段树一样给区间打上标记, pushdown \text{pushdown} pushdown函数会将标记下放并将这个节点的标记情况。每次询问所有拥有的线段树一共有多少个节点有标记。

n , Q ≤ 1 0 5 n,Q\leq 10^5 n,Q105

【解题思路】
对于每一次操作,不妨考虑对节点的情况分类统计贡献。以下称“被定位”为 q l ≤ l ≤ r ≤ q r ql\leq l\leq r\leq qr qllrqr的节点,我们大概可以根据操作区间将节点分为如下几个类

  • 访问到这个节点且这个节点被定位
  • 是第一类节点的祖先节点但这个节点未被定位
  • 未访问到这个节点且这个节点的父亲未被访问到
  • 未访问到这个节点但这个节点的父亲被访问到(也就是 q l &gt; r ql&gt;r ql>r q r &lt; l qr&lt;l qr<l

考虑这次操作对贡献的影响:

  • 对于第一类节点,一定会打上一个标记,那么它在所有线段树中的贡献和就是复制前贡献 + 2 i − 1 +2^{i-1} +2i1
  • 对于第二类节点,其标记一定全部会下放,因此它在复制后没有贡献,贡献和就是复制前贡献
  • 对于第三类节点,其标记不会发生变化,因此复制前后贡献相等,贡献和就是复制前贡献 × 2 \times 2 ×2
  • 对于第四类节点,其贡献和它以及它祖先标记的操作情况有关,我们不妨设它为 f i f_i fi,表示使得其到根节点路径上存在标记位 1 1 1的操作情况数,这个节点在所有线段树中的贡献总次数就要加上 f i f_i fi

考虑维护 f i f_i fi

  • 对于第一类节点,其 f i f_i fi + 2 i − 1 +2^{i-1} +2i1,因为被操作的所有树中这个节点的标记一定为 1 1 1
  • 对于第二类节点,其 f i f_i fi不变,因为其到根路径上所有标记一定全部已经被下放了。
  • 对于第三四类节点,其 f i f_i fi × 2 \times 2 ×2,因为其复制前后情况相同(因为若这个节点的祖先之前有标记,现在只是往下推一格)

那么线段树维护节点权值(只需要维护乘法标记,因为加法只是对于这个节点自己加),以及 f f f的值(需要维护加标记和乘标记)。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

【参考代码】

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+10,mod=998244353;
int pw,ans,delta;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;

namespace Math
{
	int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
	void up(int &x,int y){x=upm(x+y);}
	int mul(int x,int y){return 1ll*x*y%mod;}
}
using namespace Math;

namespace Data_Structure
{
	struct Segment
	{
		#define ls (x<<1)
		#define rs (x<<1|1)
		struct node
		{
			int v,mulv,f,addf,mulf;
		}t[N<<2];
		void build(int x,int l,int r)
		{
			t[x].mulv=t[x].mulf=1;
			if(l==r) return;
			int mid=(l+r)>>1;
			build(ls,l,mid);build(rs,mid+1,r);
		}
		void multv(int x,int v){t[x].v=mul(t[x].v,v);t[x].mulv=mul(t[x].mulv,v);}
		void multf(int x,int v){t[x].f=mul(t[x].f,v);t[x].mulf=mul(t[x].mulf,v);t[x].addf=mul(t[x].addf,v);}
		void addtf(int x,int v){up(t[x].f,v);up(t[x].addf,v);}
		void pushdown(int x)
		{
			if(t[x].mulv>1) multv(ls,t[x].mulv),multv(rs,t[x].mulv),t[x].mulv=1;
			if(t[x].mulf>1) multf(ls,t[x].mulf),multf(rs,t[x].mulf),t[x].mulf=1;
			if(t[x].addf>0) addtf(ls,t[x].addf),addtf(rs,t[x].addf),t[x].addf=0;
		}
		void update(int x,int l,int r,int L,int R)
		{
			if(L<=l && r<=R)
			{
				up(ans,-t[x].v);up(t[x].v,pw);up(t[x].mulv,t[x].mulv);
				addtf(x,pw);up(delta,t[x].v);
				return;
			}
			if(L>r || R<l) 
			{
				up(ans,-t[x].v);up(t[x].v,t[x].f);up(t[x].mulv,t[x].mulv);
				multf(x,2);up(delta,t[x].v);
				return;
			}
			pushdown(x);up(ans,-t[x].v);up(delta,t[x].v);
			int mid=(l+r)>>1;
			update(ls,l,mid,L,R);update(rs,mid+1,r,L,R);
		}
		#undef ls
		#undef rs
	}T;
}
using namespace Data_Structure;

namespace DreamLolita
{
	int n,Q;
	void solution()
	{
		n=read();Q=read();pw=1;
		T.build(1,1,n);
		while(Q--)
		{
			int op=read(),l,r;
			if(op&1) 
			{
				l=read();r=read();delta=0;
				T.update(1,1,n,l,r);//printf("%d %d %d\n",ans,delta,pw);
				up(ans,ans);up(ans,delta);up(pw,pw);
			}
			else writeln(ans);
		}
	}
}

int main()
{
#ifdef Durant_Lee
	freopen("LOJ3043.in","r",stdin);
	freopen("LOJ3043.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值