LibreOJ #3043.「ZJOI2019」线段树 线段树

题意

你猜

分析

显然答案就是所有操作子集的标记数之和。
假设当前每个点对答案的贡献都已知,考虑新加入一个操作,设为第 k k k个操作。
下面把点分为5类来讨论。
1、对于经过但没被定位到的节点,其贡献不变。
2、对于被定位到的点,其贡献加上 2 k − 1 2^{k-1} 2k1
3、对于被定位到的点的子树中的点,其贡献乘2。
4、对于是第1类点的儿子且不属于上面三类点的节点,其贡献的增量为有多少个操作子集使得根到该节点的链中至少有一个节点有标记。
5、对于位于第4类点子树中的点,其贡献乘2。
发现我们只要多维护一个变量记录有多少个操作子集使得根到该点的路径中至少一个点有标记,这个变量同样可以通过讨论+打标记来维护。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ls d<<1
#define rs d<<1|1

typedef long long LL;

const int N=100005;
const int MOD=998244353;

int n,m,bin,now,tag1[N*8],tag2[N*8],tag3[N*8],val[N*8],f[N*8],ans[N*8];

void mark1(int d,int w)
{
	tag1[d]=(LL)tag1[d]*w%MOD;
	val[d]=(LL)val[d]*w%MOD;
	ans[d]=(LL)ans[d]*w%MOD;
}

void mark2(int d,int w)
{
	tag2[d]=(LL)tag2[d]*w%MOD;
	tag3[d]=(LL)tag3[d]*w%MOD;
	f[d]=(LL)f[d]*w%MOD;
}

void mark3(int d,int w)
{
	(tag3[d]+=w)%=MOD;
	(f[d]+=w)%=MOD;
}

void pushdown(int d)
{
	int w=tag1[d];tag1[d]=1;
	mark1(ls,w);mark1(rs,w);
	w=tag2[d];tag2[d]=1;
	mark2(ls,w);mark2(rs,w);
	w=tag3[d];tag3[d]=0;
	mark3(ls,w);mark3(rs,w);
}

void updata(int d)
{
	ans[d]=(ans[ls]+ans[rs])%MOD;
	(ans[d]+=val[d])%=MOD;
}

void work(int d)
{
	(val[d]+=f[d])%=MOD;
	mark2(d,2);
	pushdown(d);
	mark1(ls,2);mark1(rs,2);
	updata(d);
}

void solve(int d,int l,int r,int x,int y)
{
	pushdown(d);
	if (x<=l&&r<=y)
	{
		(val[d]+=bin)%=MOD;(f[d]+=bin)%=MOD;
		mark1(ls,2);mark3(ls,bin);
		mark1(rs,2);mark3(rs,bin);
		updata(d);
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) solve(ls,l,mid,x,y);
	else work(ls);
	if (y>mid) solve(rs,mid+1,r,x,y);
	else work(rs);
	updata(d);
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n*4;i++) tag1[i]=tag2[i]=1;
	for (int i=1;i<=m;i++)
	{
		int op;scanf("%d",&op);
		if (op==2) printf("%d\n",ans[1]);
		else
		{
			bin=(!bin?1:bin*2%MOD);
			int l,r;scanf("%d%d",&l,&r);
			solve(1,1,n,l,r);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值