点此看题面

大致题意: 给你一棵线段树,两种操作。一种操作将每棵线段树复制成两个,然后在这两个线段树中的一个上面进行\(Modify(l,r)\)。另一种操作询问所有线段树的\(tag\)总和。

大力分类讨论

我们考虑用线段树来进行维护。

定义一个\(f_{rt}\),表示在当前所有线段树中编号为\(rt\)的节点上的\(tag\)之和

然后对于每次修改,就需要大力分类讨论,来计算新增加的\(2^{t-1}\)棵树对\(f_x\)的贡献

  • 当这个节点未被访问到

    显然,就相当于此次操作对该节点没有任何影响,也就是新产生的\(2^{t-1}\)棵树中\(tag\)不会发生任何变化,因此直接将\(f_{rt}*=2\)即可。

  • 当这个节点是被修改的节点

    如果这个节点是被修改的节点,则显然新产生的\(2^{t-1}\)棵树中这个点会被赋值为\(1\),因此将\(f_{rt}+=2^{t-1}\)即可。

  • 当这个节点被访问到且进行过\(PushDown\)操作

    由于进行过了\(PushDown\),则显然新产生的\(2^{t-1}\)棵树中这个点会被赋值为\(0\),因此\(f_{rt}\)不会发生任何变化。

  • 当这个节点被访问到但未进行任何操作

    如果这个节点被访问到但未进行任何操作,那么这个点的\(tag\)值就取决于这个节点到根节点的路径上(包括这个节点)是否存在至少一个节点\(tag\)值为\(1\),因为这样就能经过若干次\(PushDown\)影响到这一位上了。

    而这也是最复杂的一种情况,下面会单独对这种情况进行进一步的讨论。

处理被访问到但未进行任何操作的节点

考虑再记录一个\(g_{rt}\),表示当前所有线段树中编号为\(rt\)的节点到根节点的路径上(包括该节点)存在至少一个节点\(tag\)值为\(1\)的方案数

则这样一来在这种情况下我们就可以直接将\(f_{rt}+=g_{rt}\)了。

但就有了一个新的问题,如何维护\(g_{rt}\)?

于是又要进行一波与先前类似的分类讨论。

  • 当这个节点未被访问到

    同样,就相当于此次操作对该节点没有任何影响,因此直接将\(g_{rt}*=2\)即可。

  • 当这个节点是被修改的节点

    如果这个节点是被修改的节点,则显然新产生的\(2^{t-1}\)棵树中这个点会被赋值为\(1\),因此新产生的\(2^{t-1}\)棵树中这个点到根节点的路径上必然存在\(tag\)值为\(1\)的点(即这个点本身),因此将\(g_{rt}+=2^{t-1}\)即可。

  • 当这个节点被访问到且进行过\(PushDown\)操作

    由于进行过了\(PushDown\),则显然这个点到根的路径上的所有节点全都\(PushDown\)过,因此新产生的\(2^{t-1}\)棵树中这个点到根节点的路径上不存在\(tag\)值为\(1\)的点,因此\(g_{rt}\)不会发生任何变化。

  • 当这个节点被访问到但未进行任何操作

    将\(g_{rt}*=2\)即可。

整理+优化

接下来,我们来整理一下上面的内容:

情况 \(f_{rt}\) \(g_{rt}\)
当这个节点未被访问到 \(f_{rt}*=2\) \(g_{rt}*=2\)
当这个节点是被修改的节点 \(f_{rt}+=2^{t-1}\) \(g_{rt}+=2^{t-1}\)
当这个节点被访问到且进行过\(PushDown\)操作 无变化 无变化
当这个节点被访问到但未进行任何操作 \(f_{rt}+=g_{rt}\) \(g_{rt}*=2\)

考虑到对于未访问到的节点,我们一律都是将答案乘\(2\)的。

那么,我们能不能换一种思维,即每次不修改未被访问到的节点,而是把其余三种情况时的\(f_{rt}\)和\(g_{rt}\)除以\(2\),然后在输出答案时把答案乘上\(2^t\)。

则就得到了这样一个新表格:

情况 \(f_{rt}\) \(g_{rt}\)
当这个节点未被访问到 无变化 无变化
当这个节点是被修改的节点 \(f_{rt}=\frac{f_{rt}+1}2\) \(g_{rt}=\frac{g_{rt}+1}2\)
当这个节点被访问到且进行过\(PushDown\)操作 无变化 无变化
当这个节点被访问到但未进行任何操作 \(f_{rt}=\frac{f_{rt}+g_{rt}}2\) 无变化

这样就方便许多。

维护\(g\)的修改

但是,我们还是要注意,要在\(PushDown\)的同时维护\(g\)的修改。

我们用\(tag_{rt}\)来记录当前节点的\(g\)在上一次\(PushDown\)后被修改过多少次

则我们需要将\(rt<<1\)和\(rt<<1|1\)的\(g\)分别进行\(tag_{rt}\)次\(g=\frac{g+1}2\)。

设要进行\(tag_{rt}\)次\(g=\frac{g+1}2\)的点为\(k\),易得最终结果为:

\[\frac {g_k+2^{tag_{rt}-1}+2^{tag_{rt}-2}+...+2^{tag_{rt}}}{2^{tag_{rt}}}
\]

式子的后半部分显然可以用等底数列求和公式化简,得到:

\[\frac {g_k+{2^{tag_{rt}}-1}}{2^{tag_{rt}}}=\frac {g_k}{2^{tag_{rt}}}+1-\frac 1{2^{tag_{rt}}}
\]

分母可以预处理\(2\)的幂的逆元,然后就差不多了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
#define Shl(x) ((x<<=1)>=X&&(x-=X))
using namespace std;
int n;
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
I void clear() {fwrite(FO,1,C,stdout),C=0;}
}F;
class SegmentTree//线段树
{
private:
#define PD(x) O[x].t&&\//下传标记
(\
O[x<<1].g=((1LL*O[x<<1].g*I2[O[x].t]%X+1-I2[O[x].t])%X+X)%X,\//更新左儿子g值
O[x<<1|1].g=((1LL*O[x<<1|1].g*I2[O[x].t]%X+1-I2[O[x].t])%X+X)%X,\//更新右儿子g值
O[x<<1].t+=O[x].t,O[x<<1|1].t+=O[x].t,O[x].t=0\//下传修改次数
)
int ans,pw,I2[N+5];struct node {int f,g,t;}O[N<<2];
I void upt(CI l,CI r,CI rt,CI tl,CI tr)//修改
{
Dec(ans,O[rt].f),O[rt].f=1LL*O[rt].f*I2[1]%X,O[rt].g=1LL*O[rt].g*I2[1]%X;//先删除原来贡献,并将当前f,g除以2
if(tl<=l&&r<=tr) Inc(O[rt].f,I2[1]),Inc(O[rt].g,I2[1]);//分类讨论
else if(tr<l||r<tl) Inc(O[rt].f,O[rt].g),Shl(O[rt].g);
if(Inc(ans,O[rt].f),tr<l||r<tl) return;if(tl<=l&&r<=tr) return (void)(++O[rt].t);//加回新答案,然后模拟线段树过程
PD(rt);RI mid=l+r>>1;upt(l,mid,rt<<1,tl,tr),upt(mid+1,r,rt<<1|1,tl,tr);
}
public:
I SegmentTree() {pw=I2[0]=1,I2[1]=X+1>>1;}
I void Init(CI x) {for(RI i=2;i<=x;++i) I2[i]=1LL*I2[i-1]*I2[1]%X;}//预处理2的幂的逆元
I void Update(CI x,CI y) {upt(1,n,1,x,y),Shl(pw);}
I int GetAns() {return 1LL*ans*pw%X;}//最后答案乘上2^t
}S;
int main()
{
RI Qtot,op,x,y;F.read(n,Qtot),S.Init(Qtot);
W(Qtot--) F.read(op),op^2?(F.read(x,y),S.Update(x,y)):F.writeln(S.GetAns());
return F.clear(),0;
}

【洛谷5280】[ZJOI2019] 线段树(线段树大力分类讨论)的更多相关文章

  1. 洛谷 P3373 【模板】线段树 2

    洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...

  2. 洛谷P5280 [ZJOI2019]线段树(线段树)

    题面 传送门 题解 考场上就这么一道会做的其它连暴力都没打--活该爆炸-- 首先我们得看出问题的本质:有\(m\)个操作,总共\(2^m\)种情况分别对应每个操作是否执行,求这\(2^m\)棵线段树上 ...

  3. 洛谷P3372 【模板】线段树 1

    P3372 [模板]线段树 1 153通过 525提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 [模板]线段树1(AAAAAAAAA- [模板]线段树1 洛谷 ...

  4. 洛谷P4891 序列(势能线段树)

    洛谷题目传送门 闲话 考场上一眼看出这是个毒瘤线段树准备杠题,发现实在太难调了,被各路神犇虐哭qwq 考后看到各种优雅的暴力AC......宝宝心里苦qwq 思路分析 题面里面是一堆乱七八糟的限制和性 ...

  5. 洛谷 P2574 XOR的艺术(线段树 区间异或 区间求和)

    To 洛谷.2574 XOR的艺术 题目描述 AKN觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏.在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下 1. 拥有一个伤害串为长度为n的 ...

  6. 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块

    !!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...

  7. Bzoj5294/洛谷P4428 [Bjoi2018]二进制(线段树)

    题面 Bzoj 洛谷 题解 考虑一个什么样的区间满足重组之后可以变成\(3\)的倍数.不妨设\(tot\)为一个区间内\(1\)的个数.如果\(tot\)是个偶数,则这个区间一定是\(3\)的倍数,接 ...

  8. 【题解】洛谷P1198 [JSOI2008] 最大数(线段树)

    洛谷P1198:https://www.luogu.org/problemnew/show/P1198 思路 一道水水的线段树 20分钟A掉 这道题只涉及到单点修改和区间查询 所以这道题甚至不用Laz ...

  9. bzoj3064/洛谷P4314 CPU监控【线段树】

    好,长草博客被催更了[?] 我感觉这题完全可以当作线段树3 线段树2考加法和乘法标记的下放顺序,这道题更丧心病狂[?] 很多人可能跟我一样,刚看到这道题秒出思路:打一个当前最大值一个历史最大值不就完事 ...

随机推荐

  1. PIE SDK专题制图下屏幕坐标转地图坐标

    1.    功能简介 PIESDK提供了专题制图下鼠标屏幕坐标转地图坐标功能. 2. 功能实现说明 2.1屏幕坐标转地图坐标 此功能用到了IPageLayout.ToMapPoint()方法,它的主要 ...

  2. 让android系统中任意一个view变成进度条

    1.效果 2.进度条背景drawable文件 结束后可以恢复原背景. <?xml version="1.0" encoding="utf-8"?> ...

  3. bind-named

    main-book: http://www.zytrax.com/books/dns resolv.conf: http://dns-learning.twnic.net.tw/bind/intro4 ...

  4. Coursera 机器学习 第8章(下) Dimensionality Reduction 学习笔记

    8 Dimensionality Reduction8.3 Motivation8.3.1 Motivation I: Data Compression第二种无监督问题:维数约简(Dimensiona ...

  5. js 用简单案例举模态对话框弹出

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  6. 【iptables】规则的试验和查看

    1.存在桥接,查看桥接访客网络规则 ebtables -L 可以看到对不同的链的策略

  7. 什么是PV,什么是UV,什么是IP. 流量统计的各种数据!

    pv流量 什么是PV? 解答:PV是指页面刷新的次数,每一次页面刷新,就算做一次pv流量. PV高一定代表来访者多吗? 解答:不一定如此,一般来说,PV与来访者的数量成正比,但是PV并不直接决定页面的 ...

  8. node会话管理——cookie-parser

    cookie是由服务器发送给客户端(浏览器)的小量信息. 我们知道,平时上网时都是使用无状态的HTTP协议传输出数据,这意味着客户端与服务端在数据传送完成后就会中断连接.这时我们就需要一个一直保持会话 ...

  9. spring boot 2.0.0 + mybatis 报:Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required

    spring boot 2.0.0 + mybatis 报:Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required 无法启动 ...

  10. 文章点赞功能(Ajax)

    一.文章点赞样式构建 1.将base.html的css样式改为外部引入 将base.html的内嵌样式删除,改为使用 HTML 头部的 <head> 标签对中使用<link>标 ...