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;
}
「我不敢下苦功琢磨自己,怕终于知道自己并非珠玉;然而心中既存着一丝希冀,便又不肯甘心与瓦砾为伍。」