ZJOI2019 线段树
ZJOI2019 线段树
题意:
题目传送门
题解:
来讲一个非常卡常的矩阵+线段树做法。首先转化一下题意,直接将\(2^m\)棵线段树建出来一定是不现实的,我们对于每一个节点,记录这个节点在所有线段树中带有标记的次数,这样所有节点的权值之和就是答案了。
接下来考虑如何维护这个答案,由于每一个节点及其祖先的带标记情况只有四种:
- 当前节点带标记,祖先也带标记
- 当前节点不带标记,祖先带标记
- 当前节点带标记,祖先不带标记
- 当前节点不带标记,祖先也不带标记
实际上\(1.3\)两种状况是可以合起来的,因为可以发现之后转移当中,\(1.3\)的转移是相同的,所以我们可以将他们合起来(主要是合并起来会被评测的老年机子卡成\(40\)……)。所以我们当前记录\(f[i][0/1/2]\)表示\(i\)这个节点三种状态(分别对应上面四种情况的\(4, 2, 1 + 3\))的方案数。接下来考虑对于一个修改操作,我们的转移方法。我们假设当前节点为\(u\),其父亲节点为\(v\),修改区间为\([l, r]\)。
- \([L_u, R_u] \subseteq [l, r]\)时,那么这个点所有祖先的标记都会下传到这个点上,并且这个点也会被打上标记,那么转移就是:
\[ \begin{cases} f'[u][2] += f[u][0] + f[u][1] + f[u][2] \end{cases} \] - \([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} \] - \([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} \] - \([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} \] - \([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 线段树相关推荐
- [ZJOI2019]线段树
[ZJOI2019]线段树 https://www.luogu.org/blog/Sooke/solution-p5280 f不够,加上g g的转移?要特别注意没有直接访问的点.分5类点. 大力分类讨 ...
- 【ZJOI2019】线段树【线段树上dp】【大讨论】
题意:有一个 [1,n][1,n][1,n] 的线段树和 mmm 个区间赋值操作,求任取一个操作的子集并按顺序在线段树上跑后线段树上有 lazy 标记的点的个数之和 模 998244353998244 ...
- 二逼平衡树——树套树(线段树套Splay平衡树)
题面 Bzoj3196 解析 线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了 思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay. ...
- 线段树——HDU - 1698
题目含义 就是初始化一堆数为1 可以经过操作把一个区间的数都改变 并求这堆数的总大小 题目分析 有一个 #include<iostream> #include<stdio.h> ...
- BZOJ.1558.[JSOI2009]等差数列(线段树 差分)
BZOJ 洛谷 首先可以把原序列\(A_i\)转化成差分序列\(B_i\)去做. 这样对于区间加一个等差数列\((l,r,a_0,d)\),就可以转化为\(B_{l-1}\)+=\(a_0\),\(B ...
- 【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横
不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路 ...
- [bzoj1582][Usaco2009 Hol]Holiday Painting 节日画画_线段树
Holiday Painting 节日画画 bzoj-1582 Usaco-2009 Hol 题目大意:给定两个n*m的01网格图.q次操作,每次将第二个网格图的子矩阵全部变成0或1,问每一次操作后两 ...
- codefores 786B. Legacy(最短路,线段树优化拆点,好题)
题目链接 B. Legacy time limit per test2 seconds memory limit per test256 megabytes inputstandard input o ...
- 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树
题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...
最新文章
- mysql主备模型,MySQL数据同步【双主热备】
- 红帽虚拟化RHEV3.2创建虚拟机(图文Step by Step)
- 不展现报表实现对报表的打印导出
- bilateral filter双边滤波器的通俗理解
- Python使用pyechart绘制3d散点图
- asp.net JavaScriptSerializer实现序列化和反序列化
- MacOS如何修复磁盘权限
- javaweb项目遇到错误
- 基于Springboot的个人健康监控管理系统
- 海明码的编码和校验方法
- 零中频接收机频率转换图_相干光接收机的相关问题
- 应用于音箱领域中的音频功放IC型号推荐
- uniapp使用picker
- h3cr4900g3安装系统_H3C R4900 G2服务器通过HDM安装系统
- CF/TC 做题计划
- 苹果xr十大隐藏功能_苹果手机有哪些隐藏小功能?【建议收藏】
- 【JAVA】科研信息管理系统
- Web中常用字体介绍
- 这才是微服务划分的正确姿势,值得学习
- python 段错误_在Python中导入模块时出现分段错误