P5280 [ZJOI2019]线段树

看了下题解,感觉有点弄懂了,但是好像还是不是很清楚,打算一边写草稿,一边写题,一边写题解,用以加深印象。

题目大意

就是你有一颗表示区间 \([1,n]\) 的线段树。一开始树上的 \(tag\) 均为 \(0\) 。你接下来会进行 \(m\) 次操作。

1 l r:假设当前手上有 \(t\) 棵线段树,你需要会把每棵线段树复制两份( \(tag\) 数组也一起复制),原先编号为 \(i\) 的线段树复制得到的两棵编号为 \(2i-1\)\(2i\) ,在复制结束后,一共有 \(2t\) 棵线段树。接着,你需要会对所有编号为奇数的线段树进行一次 \(\operatorname{Modify}(root,1,n,l,r)\)

其中 \(\operatorname{Modify}\) 操作的函数如下:

2:定义一棵线段树的权值为它上面有多少个节点 \(tag\)\(1\)。输出所有线段树的权值和。

草稿 和 题解

有一个非常 \(naive\) 的做法,就是每一次暴力复制线段树,然后进行相应的操作,但是我们发现这个方法最多只能跑 \(m=10\) ,非常不可行。

然后我们考虑这个复制线段树的操作意义在于何?他无非是求对于到当前操作选或者不选的所有方案中, \(tag\) 的数量和,这个对于我们来说就是变着法的计数,我们考虑 \(\text{dp}\)

首先我们考虑状态的设计,我们发现,对于当前已经有的线段树中,同一个节点的操作是可以一起看的,因为我们只需要知道最后的和而并不需要其中具体的值,所以我们不妨设 \(f_{i,j}\) 表示节点 \(i\) 到第 \(j\) 个操作的时候,他的数量和是多少,那么对于不同的点,他的处理方式不一样。

我打算 \(he\) 一下题解的图(不代表着我在一边看题解一边写题啊……),就用神 \(\text{Sooke}\) 的吧:

可以发现,对于一次 \(\operatorname{Modify}\) ,整个线段树的点只会有 \(5\) 种情况,我们可以分开讨论:

  • 对于白色点,即是会被执行 \(\operatorname{Pushdown}\) 的点,是不会将这个 \(tag\) 标记在这里最后留下,因为他会将其传递到自己的下方 。

  • 对于黑色点,即是你最终打 \(tag\) 的点。

  • 对于橙色点,是需要接受 \(tag\) 的点,但是不需要打 \(tag\)

  • 对于黄色点和灰色点,都是在这一次操作过程中不需要变动的点,我们只需要将其的 \(f\) 数组乘以 \(2\) 即可。

对于上述的操作,我们发现,我们还需要找出这个点上方有多少 \(tag\) 要继承。这个东西也需要维护,因为我们发现我们不能在修改的时候直接得出。我们不妨设 \(g_{i,j}\) 表示节点 \(i\) 到第 \(j\) 个操作时,有多少棵线段树在 \(i\) 点及 \(i\) 点上方的节点中有 \(tag\) 。同样的,对于这个东西的维护,对于不同的节点也是不一样的。

  • 对于白色点,我们发现这个东西在进行操作的那一半线段树中是会被清零的,因为他们节点的 \(tag\) 会全部下放。

  • 对于黑色点和灰色点,由于其自己或上方的点必定会打上 \(tag\) ,所以 \(g\) 的增量就是线段树个数的增量,即 \(2^j\)

  • 对于黄色点和橙色点,由于并没有加 \(tag\) 或进行下传 ,同时其上方的点的 \(tag\) 也不会有变,所以说对于他来说,自己的 \(g\) 并没有变化,乘以 \(2\) 即可。

然后发现我们就可以维护了。

如果直接判断点的颜色再进行相应的操作是 \(O(n^2)\) 的,时间还是不满足,所以我们考虑在线段树上维护 \(\text{dp}\) 。然后应该就可以做了。具体的操作方式可以看代码,虽然还没写完。

代码如下

#include<bits/stdc++.h>
using namespace std;
#define Lint long long
const int N=1e5+5,M=1e5+5;
const Lint MOD=998244353;
int n,m,cnt=0;
Lint ksm[M];
struct Seg_Tree
{
	struct Node{Lint sum,f,g,tagf,tagg1,tagg2;}tr[N<<2];
	void build(int u,int l,int r)
	{
		tr[u].f=tr[u].g=tr[u].sum=0;
		tr[u].tagf=tr[u].tagg1=1,tr[u].tagg2=0;
		if(l==r) return ;
		int mid=(l+r)>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
	}
	void up(int u){tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum+tr[u].f)%MOD;}
	void updataf(int u,Lint z)
	{
		tr[u].f=(tr[u].f*z)%MOD;
		tr[u].sum=(tr[u].sum*z)%MOD;
		tr[u].tagf=(tr[u].tagf*z)%MOD;
	}
	void updatag(int u,Lint z1,Lint z2)
	{
		tr[u].g=(tr[u].g*z1%MOD+z2)%MOD;
		tr[u].tagg1=tr[u].tagg1*z1%MOD;
		tr[u].tagg2=(tr[u].tagg2*z1%MOD+z2)%MOD;
	}
	void down(int u)
	{
		updataf(u<<1,tr[u].tagf);
		updataf(u<<1|1,tr[u].tagf);
		tr[u].tagf=1;
		updatag(u<<1,tr[u].tagg1,tr[u].tagg2);
		updatag(u<<1|1,tr[u].tagg1,tr[u].tagg2);
		tr[u].tagg1=1,tr[u].tagg2=0;
	}
	void opt(int u,int l,int r,int x,int y,int now)
	{
		if(x<=l&&r<=y)
		{
			tr[u].sum=(tr[u].sum+MOD-tr[u].f)%MOD;
			tr[u].f=(tr[u].f+ksm[now-1])%MOD;
			tr[u].sum=(tr[u].sum+tr[u].f)%MOD;
			updatag(u,1,ksm[now-1]);//black
			if(l!=r)
			{
				down(u);
				updataf(u<<1,2);
				updataf(u<<1|1,2);//grey
				up(u);
			}
			return ;
		}
		int mid=(l+r)>>1;
		down(u);//white
		if(x<=mid) opt(u<<1,l,mid,x,y,now);
		else
		{
			tr[u<<1].sum=(tr[u<<1].sum+MOD-tr[u<<1].f)%MOD;
			tr[u<<1].f=(tr[u<<1].f+tr[u<<1].g)%MOD;
			tr[u<<1].sum=(tr[u<<1].sum+tr[u<<1].f)%MOD;//orange
			if(l!=mid)
			{
				down(u<<1);
				updataf(u<<2,2);
				updataf(u<<2|1,2);//yellow
				up(u<<1);
			}
			updatag(u<<1,2,0);//orange and yellow
		}
		if(y>mid) opt(u<<1|1,mid+1,r,x,y,now);
		else
		{
			tr[u<<1|1].sum=(tr[u<<1|1].sum+MOD-tr[u<<1|1].f)%MOD;
			tr[u<<1|1].f=(tr[u<<1|1].f+tr[u<<1|1].g)%MOD;
			tr[u<<1|1].sum=(tr[u<<1|1].sum+tr[u<<1|1].f)%MOD;//orange
			if(mid+1!=r)
			{
				down(u<<1|1);
				updataf((u<<1|1)<<1,2);
				updataf((u<<1|1)<<1|1,2);//yellow
				up(u<<1|1);
			}
			updatag(u<<1|1,2,0);//orange and yellow
		}
		up(u);
	}
}t;
int main()
{
	cin>>n>>m,ksm[0]=1;
	for(int i=1;i<=m;++i) ksm[i]=(ksm[i-1]<<1)%MOD;
	t.build(1,1,n);
	while(m--)
	{
		int opt,x,y;
		scanf("%d",&opt);
		if(opt==1)
		{
			scanf("%d%d",&x,&y);
			t.opt(1,1,n,x,y,++cnt);
		}
		else printf("%lld\n",t.tr[1].sum);
	}
	return 0;
}
posted @ 2020-11-16 18:47  Point_King  阅读(90)  评论(0编辑  收藏  举报