【题目链接】
【思路要点】
- 不难发现题目要求计算所有操作集合的子集对应的 t a g tag tag 数之和。
- 分开考虑每个节点上 t a g tag tag 的存在情况,我们希望设计一个能够转移的状态。
- 一种可行的方式是在状态中记录 0 / 1 0/1 0/1 二元组 ( s e l f , a n c e s t o r ) (self,ancestor) (self,ancestor) 分别表示节点本身及其任意祖先上是否存在标记。
- 一次修改将对所有点的状态进行如下转移:
- 令被区间定位到的点为 ( 1 ) (1) (1) 类点;被访问到但没有被定位到的点为 ( 2 ) (2) (2) 类点;父亲被访问到但本身没有被定位到的点为 ( 3 ) (3) (3) 类点; ( 1 ) (1) (1) 类点的子节点为 ( 0 ) (0) (0) 类点;剩余节点,也即 ( 3 ) (3) (3) 类点的子节点为 ( 4 ) (4) (4) 类点。
- 对于 ( 1 ) (1) (1) 类点,有 s e l f ⇐ 1 , a n c e s t o r ⇐ 0 self\Leftarrow 1,ancestor\Leftarrow 0 self⇐1,ancestor⇐0 。
- 对于 ( 2 ) (2) (2) 类点,有 s e l f ⇐ 0 , a n c e s t o r ⇐ 0 self\Leftarrow 0,ancestor\Leftarrow 0 self⇐0,ancestor⇐0 。
- 对于 ( 3 ) (3) (3) 类点,有 s e l f ⇐ a n c e s t o r ∣ s e l f , a n c e s t o r ⇐ 0 self\Leftarrow ancestor|self,ancestor\Leftarrow 0 self⇐ancestor∣self,ancestor⇐0 。
- 对于 ( 4 ) (4) (4) 类点,有 s e l f ⇐ s e l f , a n c e s t o r ⇐ a n c e s t o r self\Leftarrow self,ancestor\Leftarrow ancestor self⇐self,ancestor⇐ancestor 。
- 对于 ( 0 ) (0) (0) 类点,有 s e l f ⇐ s e l f , a n c e s t o r ⇐ 1 self\Leftarrow self,ancestor\Leftarrow 1 self⇐self,ancestor⇐1 。
- 以上转移均可以写为矩阵的形式,因此直接用线段树维护 d p dp dp 向量,以矩阵作为标记即可。
- 时间复杂度 O ( 4 3 × M L o g N ) O(4^3\times MLogN) O(43×MLogN) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int P = 998244353; const int Trans[5][4][4] = { {{1, 0, 1, 0}, {0, 1, 0, 1}, {0, 0, 2, 0}, {0, 0, 0, 2}}, {{1, 1, 0, 0}, {0, 2, 0, 0}, {0, 1, 1, 0}, {0, 1, 0, 1}}, {{2, 0, 0, 0}, {1, 1, 0, 0}, {1, 0, 1, 0}, {1, 0, 0, 1}}, {{2, 0, 0, 0}, {0, 2, 0, 0}, {0, 1, 1, 0}, {0, 1, 0, 1}}, {{2, 0, 0, 0}, {0, 2, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 2}} }; // 0: self <- self, ancestor <- 1 // 1: self <- 1, ancestor <- 0 // 2: self <- 0, ancestor <- 0 // 3: self <- ancestor | self, ancestor <- 0 // 4: do nothing typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct vec {int a[4]; }; //0 : none, 1 : self, 2 : ancestor, 3 : both struct mat {int a[4][4]; } trans[5]; vec operator + (const vec &a, const vec &b) { vec ans; for (int i = 0; i <= 3; i++) ans.a[i] = (a.a[i] + b.a[i] > P) ? (a.a[i] + b.a[i] - P) : (a.a[i] + b.a[i]); return ans; } vec operator * (const vec &a, const mat &b) { vec ans; for (int j = 0; j <= 3; j++) { ll res = 0; for (int i = 0; i <= 3; i++) res += 1ll * a.a[i] * b.a[i][j]; ans.a[j] = res % P; } return ans; } mat operator * (const mat &a, const mat &b) { mat ans; for (int i = 0; i <= 3; i++) for (int k = 0; k <= 3; k++) { ll res = 0; for (int j = 0; j <= 3; j++) res += 1ll * a.a[i][j] * b.a[j][k]; ans.a[i][k] = res % P; } return ans; } mat unit() { mat ans; for (int i = 0; i <= 3; i++) for (int j = 0; j <= 3; j++) ans.a[i][j] = i == j; return ans; } struct SegmentTree { struct Node { mat tag; bool unit; int lc, rc; vec val, sum; } a[MAXN * 2]; int n, root, size; void update(int root) { a[root].sum = a[root].val; if (a[root].lc) a[root].sum = a[root].sum + a[a[root].lc].sum; if (a[root].rc) a[root].sum = a[root].sum + a[a[root].rc].sum; } void build(int &root, int l, int r) { root = ++size; a[root].tag = unit(); a[root].unit = true; a[root].val.a[0] = 1; a[root].sum.a[0] = 1; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } void pushdown(int root) { if (!a[root].unit && a[root].lc) { int tmp = a[root].lc; a[tmp].unit = false; a[tmp].val = a[tmp].val * a[root].tag; a[tmp].sum = a[tmp].sum * a[root].tag; a[tmp].tag = a[tmp].tag * a[root].tag; tmp = a[root].rc; a[tmp].unit = false; a[tmp].val = a[tmp].val * a[root].tag; a[tmp].sum = a[tmp].sum * a[root].tag; a[tmp].tag = a[tmp].tag * a[root].tag; a[root].unit = true; a[root].tag = unit(); } } void puttag(int root, mat trans) { a[root].tag = a[root].tag * trans; a[root].sum = a[root].sum * trans; a[root].val = a[root].val * trans; a[root].unit = false; } void pushed(int root) { pushdown(root); a[root].val = a[root].val * trans[3]; if (a[root].lc) puttag(a[root].lc, trans[4]); if (a[root].rc) puttag(a[root].rc, trans[4]); update(root); } void modify(int root, int l, int r, int ql, int qr) { pushdown(root); if (l == ql && r == qr) { a[root].val = a[root].val * trans[1]; if (a[root].lc) puttag(a[root].lc, trans[0]); if (a[root].rc) puttag(a[root].rc, trans[0]); update(root); return; } a[root].val = a[root].val * trans[2]; int mid = (l + r) / 2; if (mid >= qr) { pushed(a[root].rc); modify(a[root].lc, l, mid, ql, qr); } else if (mid + 1 <= ql) { pushed(a[root].lc); modify(a[root].rc, mid + 1, r, ql, qr); } else { modify(a[root].lc, l, mid, ql, mid); modify(a[root].rc, mid + 1, r, mid + 1, qr); } update(root); } void modify(int l, int r) { modify(root, 1, n, l, r); } int query() { return (a[root].sum.a[1] + a[root].sum.a[3]) % P; } } ST; int n, m; int main() { read(n), read(m); for (int i = 0; i <= 4; i++) { for (int j = 0; j <= 3; j++) for (int k = 0; k <= 3; k++) trans[i].a[j][k] = Trans[i][j][k]; } ST.init(n); for (int i = 1; i <= m; i++) { int opt, l, r; read(opt); if (opt == 1) { read(l), read(r); ST.modify(l, r); } else writeln(ST.query()); } return 0; }