ZJOI2019 线段树

题意:

题目传送门

题解:

来讲一个非常卡常的矩阵+线段树做法。首先转化一下题意,直接将\(2^m\)棵线段树建出来一定是不现实的,我们对于每一个节点,记录这个节点在所有线段树中带有标记的次数,这样所有节点的权值之和就是答案了。
接下来考虑如何维护这个答案,由于每一个节点及其祖先的带标记情况只有四种:

  1. 当前节点带标记,祖先也带标记
  2. 当前节点不带标记,祖先带标记
  3. 当前节点带标记,祖先不带标记
  4. 当前节点不带标记,祖先也不带标记

实际上\(1.3\)两种状况是可以合起来的,因为可以发现之后转移当中,\(1.3\)的转移是相同的,所以我们可以将他们合起来(主要是合并起来会被评测的老年机子卡成\(40\)……)。所以我们当前记录\(f[i][0/1/2]\)表示\(i\)这个节点三种状态(分别对应上面四种情况的\(4, 2, 1 + 3\))的方案数。接下来考虑对于一个修改操作,我们的转移方法。我们假设当前节点为\(u\),其父亲节点为\(v\),修改区间为\([l, r]\)。

  1. \([L_u, R_u] \subseteq [l, r]\)时,那么这个点所有祖先的标记都会下传到这个点上,并且这个点也会被打上标记,那么转移就是:
    \[ \begin{cases} f'[u][2] += f[u][0] + f[u][1] + f[u][2] \end{cases} \]
  2. \([L_u, R_u] \subset [l, r]\ \ \&\& \ \ [L_v, R_v] \subseteq [l, r]\),即当前节点属于修改区间,但是是在其祖先节点打上修改标记的这些节点。那么这个点所有情况都会被改成祖先带标记的情况,转移就是:
    \[ \begin{cases} f'[u][1] += f[u][0] + f[u][1] \\ f'[u][2] += f[u][2] \end{cases} \]
  3. \([L_u, R_u] \cap [l, r] \neq \emptyset\),即这个节点不会被打上修改标记,但是在定位修改区间的时候会被访问到的节点。这些节点及其祖先所有的标记都会下传,所有的情况都会被改成祖先与自身都不带标记的情况,转移就是:
    \[ \begin{cases} f'[u][0] += f[u][0] + f[u][1] + f[u][2] \\ \end{cases} \]
  4. \([L_u, R_u] \cap [l, r] = \emptyset \ \ \& \& \ \ [L_v, R_v] \cap [l, r] \neq \emptyset\),即这个节点不会在定位区间时被访问到,但是其父亲会被访问到,那么它的祖先如果有标记,就会下传到这个节点中,转移就是:
    \[ \begin{cases} f'[u][0] += f[u][0] \\ f'[u][2] += f[u][1] + f[u][2] \end{cases} \]
  5. \([L_u, R_u] \cap [l, r] = \emptyset \ \ \& \& \ \ [L_v, R_v] \cap [l, r] = \emptyset\),即这些节点与本次修改无关,直接把原来的方案数乘2即可,转移就是:
    \[ \begin{cases} f'[u][0] += f[u][0] \\ f'[u][1] += f[u][1] \\ f'[u][2] += f[u][2] \end{cases} \]

直接暴力转移复杂度就是\(O(n^2)\)的,我们发现\(1.3.4\)这三种转移在定位修改区间时都会被访问到,所以可以直接进行修改,然后\(2.5\)两个转移我们考虑打标记。考虑用矩阵进行维护,那么第二种转移的转移矩阵就是这样的:
\[ \left[ \begin{matrix} 1 & 1 & 0 \\ 0 & 2 & 0 \\ 0 & 0 & 2 \end{matrix} \right] \]
第三种转移的转移矩阵就是这样的:
\[ \left[ \begin{matrix} 2 & 0 & 0 \\ 0 & 2 & 0 \\ 0 & 0 & 2 \end{matrix} \right] \]
然后就是线段树打标记,查询根节点权值就行了。说实话……这个方法似乎用省选评测的老年机似乎是跑不过去的……算了我这个考场上根本没有码出来的菜鸡就不说了吧……

UPD:话说不用矩阵维护的方法似乎好写好调跑的又快啊……啊我真是菜爆了……

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 50;
const int Md = 998244353;
typedef long long ll;inline int Add(const int &x, const int &y) { return (x + y >= Md) ? (x + y - Md) : (x + y); }
inline int Sub(const int &x, const int &y) { return (x - y < 0) ? (x - y + Md) : (x - y); }
inline int Mul(const int &x, const int &y) { return (ll)x * y % Md; }
int Powe(int x, int y) {int ans = 1;while (y) {if (y & 1)ans = Mul(ans, x);x = Mul(x, x);y >>= 1;}return ans;
}int n, m;namespace Solver2 {
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)struct Mat {int v[3][3];Mat() { memset(v, 0, sizeof v); }int *operator[](int x) { return v[x]; }Mat operator*(Mat B) {Mat C;for (int k = 0; k < 3; k++) {for (int i = 0; i < 3; i++) {if (!v[i][k])continue;if (B.v[k][0])C.v[i][0] = Add(C.v[i][0], Mul(v[i][k], B.v[k][0]));if (B.v[k][1])C.v[i][1] = Add(C.v[i][1], Mul(v[i][k], B.v[k][1]));if (B.v[k][2])C.v[i][2] = Add(C.v[i][2], Mul(v[i][k], B.v[k][2]));}}return C;}void Base() {memset(v, 0, sizeof v);for (int i = 0; i < 3; i++) v[i][i] = 1;}int EquBase() {for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (i == j && v[i][j] != 1)return 0;if (i != j && v[i][j] != 0)return 0;}}return 1;}};Mat Bas, trans1, trans2;Mat va[N << 2], tag[N << 2], sum[N << 2];void Apply(int o, Mat A) {sum[o] = sum[o] * A;tag[o] = tag[o] * A;va[o] = va[o] * A;}void Push(int o) {if (tag[o].EquBase()) return;Apply(ls(o), tag[o]);Apply(rs(o), tag[o]);tag[o].Base();return;}void Update(int o) {for (int i = 0; i < 3; i++) sum[o][0][i] = Add(sum[ls(o)][0][i], sum[rs(o)][0][i]), sum[o][0][i] = Add(sum[o][0][i], va[o][0][i]);}void Pre(int o, int l, int r) {va[o][0][0] = 1;tag[o].Base();sum[o][0][0] = 1;if (l == r) return;int mid = (l + r) >> 1;Pre(ls(o), l, mid);Pre(rs(o), mid + 1, r);Update(o);}void Modify(int o, int l, int r, int L, int R) {if (l > R || r < L) {va[o][0][0] = Add(va[o][0][0], va[o][0][0]);va[o][0][2] = Add(va[o][0][2], Add(va[o][0][1], va[o][0][2]));if (l == r) return (void)(Update(o));int mid = (l + r) >> 1;Push(o);Apply(ls(o), trans1);Apply(rs(o), trans1);Update(o);return;}if (L <= l && r <= R) {va[o][0][2] = Add(va[o][0][2], Add(va[o][0][2], Add(va[o][0][1], va[o][0][0])));if (l != r) {Push(o);va[ls(o)][0][1] = Add(va[ls(o)][0][1], Add(va[ls(o)][0][1], va[ls(o)][0][0]));va[ls(o)][0][2] = Add(va[ls(o)][0][2], va[ls(o)][0][2]);va[rs(o)][0][1] = Add(va[rs(o)][0][1], Add(va[rs(o)][0][1], va[rs(o)][0][0]));va[rs(o)][0][2] = Add(va[rs(o)][0][2], va[rs(o)][0][2]);int mid = (l + r) >> 1;if (l != mid) {Push(ls(o));Apply(ls(ls(o)), trans2);Apply(rs(ls(o)), trans2);}if (r != mid) {Push(rs(o));Apply(ls(rs(o)), trans2);Apply(rs(rs(o)), trans2);}Update(ls(o));Update(rs(o));}Update(o);return;}Push(o);int mid = (l + r) >> 1;va[o][0][0] = Add(va[o][0][0], Add(va[o][0][0], Add(va[o][0][1], va[o][0][2])));Modify(ls(o), l, mid, L, R);Modify(rs(o), mid + 1, r, L, R);Update(o);return;}void main() {Bas.Base();for (int i = 0; i < 3; i++) trans1[i][i] = 2;trans2[0][1] = trans2[0][0] = 1;trans2[1][1] = trans2[2][2] = 2;Pre(1, 1, n);for (int i = 1, tp; i <= m; i++) {scanf("%d", &tp);if (tp == 2)printf("%d\n", sum[1][0][2]);else {int l, r;scanf("%d%d", &l, &r);Modify(1, 1, n, l, r);}}}
}int main() {scanf("%d%d", &n, &m);Solver2::main();return 0;
}

转载于:https://www.cnblogs.com/Apocrypha/p/10644534.html

ZJOI2019 线段树相关推荐

  1. [ZJOI2019]线段树

    [ZJOI2019]线段树 https://www.luogu.org/blog/Sooke/solution-p5280 f不够,加上g g的转移?要特别注意没有直接访问的点.分5类点. 大力分类讨 ...

  2. 【ZJOI2019】线段树【线段树上dp】【大讨论】

    题意:有一个 [1,n][1,n][1,n] 的线段树和 mmm 个区间赋值操作,求任取一个操作的子集并按顺序在线段树上跑后线段树上有 lazy 标记的点的个数之和 模 998244353998244 ...

  3. 二逼平衡树——树套树(线段树套Splay平衡树)

    题面 Bzoj3196 解析 线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了 思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay. ...

  4. 线段树——HDU - 1698

    题目含义 就是初始化一堆数为1 可以经过操作把一个区间的数都改变 并求这堆数的总大小 题目分析 有一个 #include<iostream> #include<stdio.h> ...

  5. BZOJ.1558.[JSOI2009]等差数列(线段树 差分)

    BZOJ 洛谷 首先可以把原序列\(A_i\)转化成差分序列\(B_i\)去做. 这样对于区间加一个等差数列\((l,r,a_0,d)\),就可以转化为\(B_{l-1}\)+=\(a_0\),\(B ...

  6. 【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

    不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路 ...

  7. [bzoj1582][Usaco2009 Hol]Holiday Painting 节日画画_线段树

    Holiday Painting 节日画画 bzoj-1582 Usaco-2009 Hol 题目大意:给定两个n*m的01网格图.q次操作,每次将第二个网格图的子矩阵全部变成0或1,问每一次操作后两 ...

  8. codefores 786B. Legacy(最短路,线段树优化拆点,好题)

    题目链接 B. Legacy time limit per test2 seconds memory limit per test256 megabytes inputstandard input o ...

  9. 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树

    题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...

最新文章

  1. mysql主备模型,MySQL数据同步【双主热备】
  2. 红帽虚拟化RHEV3.2创建虚拟机(图文Step by Step)
  3. 不展现报表实现对报表的打印导出
  4. bilateral filter双边滤波器的通俗理解
  5. Python使用pyechart绘制3d散点图
  6. asp.net JavaScriptSerializer实现序列化和反序列化
  7. MacOS如何修复磁盘权限
  8. javaweb项目遇到错误
  9. 基于Springboot的个人健康监控管理系统
  10. 海明码的编码和校验方法
  11. 零中频接收机频率转换图_相干光接收机的相关问题
  12. 应用于音箱领域中的音频功放IC型号推荐
  13. uniapp使用picker
  14. h3cr4900g3安装系统_H3C R4900 G2服务器通过HDM安装系统
  15. CF/TC 做题计划
  16. 苹果xr十大隐藏功能_苹果手机有哪些隐藏小功能?【建议收藏】
  17. 【JAVA】科研信息管理系统
  18. Web中常用字体介绍
  19. 这才是微服务划分的正确姿势,值得学习
  20. python 段错误_在Python中导入模块时出现分段错误

热门文章

  1. 十个Android Material Design库
  2. 全视频沟通不再远 罗技为中国企业开启视频协作新格局
  3. List与数组的相互转换
  4. CATransform3D 特效详解
  5. (MYSQL) Unknown table 'a' in MULTI DELETE的解决办法
  6. 战神级CTO直招产品技术小鲜肉,“悦家”团队带你玩转家装O2O
  7. dxf文件预览打开编辑相关控件推荐
  8. 【读书笔记】Java基础学习之目录
  9. 在线编辑ewebeditor
  10. F5紧急修复严重的 BIG-IP 预认证 RCE 漏洞