ZJOI2019 线段树

mac2022-06-30  142

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

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)