题目链接:洛谷

题目描述:【比较复杂,建议看原题】


这道题太神仙了,线段树上做树形dp。

根据树形dp的套路,都是按照转移的不同情况给节点分类。这里每次modify的时候对于节点的影响也不同,所以我们考虑分类。

(这里借用一张图,%%%sooke大佬)

我们发现每次modify的时候对节点的影响有这5种节点。(因为每棵线段树的形态一致,所以我们只用一棵线段树)

一类点(白色):在 modify 操作中,被半覆盖的点。

二类点(深灰):在 modify 操作中,被全覆盖的点,并且能被遍历到。

三类点(橙色):在 modify 操作中,未被覆盖的点,并且可以得到 pushdown 来的标记。

四类点(浅灰):在 modify 操作中,被全覆盖的点,并且不能被遍历到。

五类点(黄色):在 modify 操作中,未被覆盖的点,并且不可能得到 pushdown 来的标记。

设编号为$x$的节点,在$i$次modify之后,生成的这$2^i$棵线段树中,有$f_{x,i}$棵在这个节点上有标记.

我们对于每一类都推一下。

一类点:因为没有全覆盖,所以新的这些线段树在$x$上是没有标记的。所以$f_{x,i}=f_{x,i-1}+0$

二类点:因为全覆盖了,所以新的这些线段树在$x$上必有标记。所以$f_{x,i}=f_{x,i-1}+2^{i-1}$

三类点:因为要pushdown,所以$x$上有标记当且仅当之前的线段树中$x$及$x$的祖先至少有一个有标记。所以$f_{x,i}=f_{x,i-1}+\ldots$.

Oh,no!出锅了,这里不知道要加多少。

但是仔细一想,发现其实这个也是可以dp的。


设编号为$x$的节点,在$i$次modify之后,生成的这$2^i$棵线段树中,在$x$及$x$的祖先上,没有一个有标记的线段树有$g_{x,i}$棵。

然后继续推。

一类点:对于$g$,因为没有全覆盖,所以$x$和$x$的祖父也是没有标记的。

$$f_{x,i}=f_{x,i-1}+0,g_{x,i}=g_{x,i-1}+2^{i-1}$$

二类点:对于$g$,因为全覆盖了,所以$x$必定有标记。

$$f_{x,i}=f_{x,i-1}+2^{i-1},g_{x,i}=g_{x,i-1}+0$$

三类点:对于$g$,因为未被覆盖,所以对$x$及$x$的祖先并没有影响。

$$f_{x,i}=f_{x,i-1}+2^{i-1}-g_{x,i-1},g_{x,i}=g_{x,i-1}+g_{x,i-1}$$

四类点:对于$f$,因为没有被遍历到,所以对$x$的标记没有影响;对于$g$,因为被全覆盖,所以祖先上必定有标记。

$$f_{x,i}=f_{x,i-1}+f_{x,i-1},g_{x,i}=g_{x,i-1}+0$$

五类点:$f$同四类点;对于$g$,因为没有被覆盖,所以祖先上必定没有标记。

$$f_{x,i}=f_{x,i-1}+f_{x,i-1},g_{x,i}=g_{x,i-1}+g_{x,i-1}$$

初值:$f_{x,0}=0,g_{x,0}=1$

答案是整个线段树所有节点$f$之和,也可以用线段树顺便维护。

但是如果暴力转移就肯定是$O(n)$的,但其实比较复杂的一、二、三类点都至多有$O(\log n)$个,而四、五类点都是区间乘法就行。所以前者暴力转移,后者直接打懒标记。


如果您看得一脸懵逼(我的语文太差),那么看下面。

这里说的对点分类,是按照每一步操作(modify)对每个节点状态的影响(dp转移方程)来分类的。

所以类别并不属于dp状态的一维,只是一个分类讨论的过程。(通常的树形dp都是这个思路,大家可以好好理解一下)

如果您还是看不懂,那就可以看代码了。

 #include<cstdio>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = , mod = ;
inline int add(int a, int b){int res = a + b; if(res >= mod) res -= mod; return res;}
inline int dec(int a, int b){int res = a - b; if(res < ) res += mod; return res;}
int n, m, k = , f[N], g[N], lf[N], lg[N], sf[N];
inline void pushf(int x, int val = ){
f[x] = (LL) f[x] * val % mod;
lf[x] = (LL) lf[x] * val % mod;
sf[x] = (LL) sf[x] * val % mod;
}
inline void pushg(int x, int val = ){
g[x] = (LL) g[x] * val % mod;
lg[x] = (LL) lg[x] * val % mod;
}
inline void pushdown(int x){
if(lf[x] != ) pushf(x << , lf[x]), pushf(x << | , lf[x]), lf[x] = ;
if(lg[x] != ) pushg(x << , lg[x]), pushg(x << | , lg[x]), lg[x] = ;
}
inline void pushup(int x){
sf[x] = add(sf[x << ], add(sf[x << | ], f[x]));
}
inline void build(int x, int L, int R){
g[x] = lf[x] = lg[x] = ;
if(L == R) return;
int mid = L + R >> ;
build(x << , L, mid);
build(x << | , mid + , R);
}
inline void modify(int x, int L, int R, int l, int r){
pushdown(x);
if(l <= L && R <= r){
f[x] = add(f[x], k);
pushf(x << ); pushf(x << | );
pushup(x);
return;
}
int mid = L + R >> , lx = x << , rx = x << | ;
g[x] = add(g[x], k);
if(r <= mid){
modify(lx, L, mid, l, r);
pushdown(rx);
f[rx] = add(f[rx], dec(k, g[rx]));
g[rx] = add(g[rx], g[rx]);
pushf(rx << ); pushf(rx << | );
pushg(rx << ); pushg(rx << | );
pushup(rx);
} else if(mid < l){
modify(rx, mid + , R, l, r);
pushdown(lx);
f[lx] = add(f[lx], dec(k, g[lx]));
g[lx] = add(g[lx], g[lx]);
pushf(lx << ); pushf(lx << | );
pushg(lx << ); pushg(lx << | );
pushup(lx);
} else {
modify(lx, L, mid, l, r);
modify(rx, mid + , R, l, r);
}
pushup(x);
}
int main(){
scanf("%d%d", &n, &m);
build(, , n);
for(Rint i = ;i <= m;i ++){
int opt, l, r;
scanf("%d", &opt);
if(opt == ) printf("%d\n", sf[]);
else {
scanf("%d%d", &l, &r);
modify(, , n, l, r); k = add(k, k);
}
}
}

Luogu5280

P5280 [ZJOI2019]线段树的更多相关文章

  1. 洛谷P5280 [ZJOI2019]线段树

      https://www.luogu.org/problemnew/show/P5280 省选的时候后一半时间开这题,想了接近两个小时的各种假做法,之后想的做法已经接近正解了,但是有一些细节问题理不 ...

  2. Luogu P5280 [ZJOI2019]线段树

    送我退役的神题,但不得不说是ZJOIDay1最可做的一题了 先说一下考场的ZZ想法以及出来后YY的优化版吧 首先发现每次操作其实就是统计出增加的节点个数(原来的不会消失) 所以我们只要统计出线段树上每 ...

  3. 洛谷 P5280 - [ZJOI2019]线段树(线段树+dp,神仙题)

    题面传送门 神仙 ZJOI,不会做啊不会做/kk Sooke:"这八成是考场上最可做的题",由此可见 ZJOI 之毒瘤. 首先有一个非常显然的转化,就是题目中的"将线段树 ...

  4. 洛谷P5280 [ZJOI2019]线段树 [线段树,DP]

    传送门 无限Orz \(\color{black}S\color{red}{ooke}\)-- 思路 显然我们不能按照题意来每次复制一遍,而多半是在一棵线段树上瞎搞. 然后我们可以从\(modify\ ...

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

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

  6. [ZJOI2019]线段树

    题目大意 一开始有一棵线段树,然后有一个操作序列,问执行这个操作序列的所有子集时线段树上有标记的节点个数和. 题解 其实我们把它除以\(2^m\)后发现就是有标记节点的期望个数. 然后套路的根据期望的 ...

  7. Luogu5280 ZJOI2019线段树(线段树)

    容易发现相当于求2m种操作序列所得的每种线段树tag数量之和.显然考虑每个点的贡献,也即有多少种方案会使该点上有tag.可以将点分为四类: 1.修改时被经过且有儿子被修改的节点 2.修改时被经过且没有 ...

  8. 【洛谷5280】[ZJOI2019] 线段树(线段树大力分类讨论)

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

  9. Luogu5280 [ZJOI2019] 线段树 【线段树】

    题目分析: 这题除了分类讨论就没啥了... 容易发现问题实际就是所有操作选和不选按顺序执行的所有答案和.考虑每个点在多少种情况下会有tag. 那么,考虑新插入一个[l,r],所有有交集的点都会被清空, ...

随机推荐

  1. mysql触发器详解 mysql触发器

    目录 21.1. CREATE TRIGGER语法 21.2. DROP TRIGGER语法 21.3. 使用触发程序 MySQL 5.1包含对触发程序的支持.触发程序是与表有关的命名数据库对象,当表 ...

  2. 突破拐点:企业成长的S曲线

    1.企业成长的不同时期 初创期:初创前几年,最重要的创业激情,靠“蓝色小药丸”,断层期切入,不断试错玩命干(产品定位.商业模式.融资源.找关系.辅渠道),并最终完成赢利模式,得以生存. 成长期:通过快 ...

  3. 下载Chrome商店和Youtube资源

    下载chrome浏览器插件 站点:http://cooal.cn/crx.php 操作步骤: 1.打开扩展介绍页面 (在 三道杠图标>工具>扩展程序 里相应扩展的"访问网站&qu ...

  4. 具体解释MVP矩阵之ViewMatrix

    矩阵推导 ViewMatrix用于直接将World坐标系下的坐标转换到Camera坐标系下.已知相机的坐标系.还有相机在世界空间下的坐标.就能够求出ViewMatrix.以下进行具体推导. 令UVN为 ...

  5. C语言 · 求先序遍历

    算法训练 求先序排列   时间限制:1.0s   内存限制:256.0MB        锦囊1 后序的最后一个字母为根结点.   问题描述 给出一棵二叉树的中序与后序排列.求出它的先序排列.(约定树 ...

  6. 【Manacher算法】最长子回文串

    [Manacher算法] 这个算法用来找出一个字符串中最长的回文子字符串. 如果采取暴力解最长回文子字符串问题,大概可以有两种思路:1. 遍历出所有子字符串找其中最长的回文 2. 从每个字符作为中心, ...

  7. Python类和实例方法和属性的动态绑定

    python中实例创建后可以给实例绑定任何属性和方法 class Student(object): pass 给实例绑定一个属性: s=Student() s.name='Michel' print ...

  8. MTK LCM的添加

    对于LCM驱动移植,一般分为三部曲: 1.硬件IO口配置: 2.确保LCM背光能够正常点亮: 3.LCM驱动移植: 硬件电路: 1.GPIO配置 打开 mediatek\dct\DrvGen.exe ...

  9. Oracle 11G 安装文档

    一. 将文件win32_11gR2_database_1of2.zip和win32_11gR2_database_2of2.zip解压. 注意:这两个文件解压到同一个目录下,即:将Components ...

  10. MySQL 全文检索 ngram插件

    InnoDB全文索引:N-gram Parser[转] MySql5.7 建立全文索引 InnoDB默认的全文索引parser非常合适于Latin,因为Latin是通过空格来分词的.但对于像中文,日文 ...