「12.14test」勇敢
给定一个长度为 \(N(N \le 10^5)\) 的 \(01\) 序列 \(a\) 以及 \(M(M \le 10^5)\) 次操作,操作有以下两种:
- \(\texttt{1 l r}\) 将区间 \([l, r]\) 的 \(0\) 变成 \(1\),\(1\) 变成 \(0\)。
- \(\texttt{2 l r}\) 询问区间 \([l, r]\) 本质不同的子序列个数。
对于每次询问输出一行表示答案。
我们首先要想到这样一个 \(\text{DP}\) :
设 \(dp_{i, 0 / 1}\) 表示 \(a[1...i]\) 的子序列中结尾为 \(0 / 1\) 的方案数,那么转移就是:
\[ \begin{cases} dp_{i, 0} = dp_{i - 1, 0} + dp_{i - 1, 1} + 1 \\ dp_{i, 1} = dp_{i - 1, 1} \end{cases} \]
这是 \(a_i = 0\) 的情况,\(a_i = 1\) 的情况类同。
然后再考虑用矩阵来表示这个转移:
\[ \begin{bmatrix} dp_{i, 0} & dp_{i, 1} & 1\end{bmatrix} = \begin{cases} \begin{bmatrix} dp_{i - 1, 0} & dp_{i - 1, 1} & 1\end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 \\ 1 & 1 & 0 \\ 1 & 0 & 1 \end{bmatrix} , a_i = 0 \\ \begin{bmatrix} dp_{i - 1, 0} & dp_{i - 1, 1} & 1\end{bmatrix} \times \begin{bmatrix} 1 & 1 & 0 \\ 0 & 1 & 0 \\ 0 & 1 & 1 \end{bmatrix} , a_i = 1 \\ \end{cases} \]
初始矩阵就是 \(\begin{bmatrix} 0 & 0 & 1 \end{bmatrix}\)
那么我们如果要求一段区间 \([l, r]\) 的答案,就可以直接用初始矩阵依次乘上 \(a_l, a_{l + 1}, \cdots, a_{r - 1}, a_{r}\) 对应的转移矩阵就可以得到答案。
那么我们可以用线段树来实现:
线段树的每一个节点存一下对应区间当前的矩阵乘积以及翻转一次后的矩阵乘积,还有反转标记。
然后具体实现看代码就是了。
参考代码:
#pragma GCC optimize("Ofast")
#include <algorithm>
#include <cstring>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
const int _ = 1e5 + 5, mod = 1e9 + 7;
typedef long long LL;
int n, m, a[_];
inline void Plus(int& a, const int& b) { (a += b) < mod ? a : a - mod; }
struct Matrix {
int a[4][4];
inline void make(int opt) {
if (opt == 0) {
a[1][1] = 1, a[1][2] = 0, a[1][3] = 0;
a[2][1] = 1, a[2][2] = 1, a[2][3] = 0;
a[3][1] = 1, a[3][2] = 0, a[3][3] = 1;
} else if (opt == 1) {
a[1][1] = 1, a[1][2] = 1, a[3][1] = 0;
a[2][1] = 0, a[2][2] = 1, a[2][3] = 0;
a[3][1] = 0, a[3][2] = 1, a[3][3] = 1;
}
else if (opt == -1) memset(a, 0, sizeof a);
else if (opt == -2) memset(a, 0, sizeof a), a[1][1] = a[2][2] = a[3][3] = 1;
}
inline Matrix operator * (const Matrix& b) const {
Matrix ans ; ans.make(-1);
for (rg int k = 1; k <= 3; ++k)
for (rg int i = 1; i <= 3; ++i)
for (rg int j = 1; j <= 3; ++j)
ans.a[i][j] = (ans.a[i][j] + 1ll * a[i][k] * b.a[k][j]) % mod;
return ans;
}
} A;
struct node { Matrix x, y; int flag; } t[_ << 2];
inline int lc(int p) { return p << 1; }
inline int rc(int p) { return p << 1 | 1; }
inline void pushup(int p) {
t[p].x = t[lc(p)].x * t[rc(p)].x, t[p].y = t[lc(p)].y * t[rc(p)].y;
}
inline void f(int p) { swap(t[p].x, t[p].y), t[p].flag ^= 1; }
inline void pushdown(int p) { if (t[p].flag) f(lc(p)), f(rc(p)), t[p].flag = 0; }
inline void build(int p = 1, int l = 1, int r = n) {
if (l == r) { t[p].x.make(a[l]), t[p].y.make(a[l] ^ 1); return ; }
int mid = (l + r) >> 1;
build(lc(p), l, mid), build(rc(p), mid + 1, r), pushup(p);
}
inline void update(int ql, int qr, int p = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr) return f(p);
int mid = (l + r) >> 1;
pushdown(p);
if (ql <= mid) update(ql, qr, lc(p), l, mid);
if (qr > mid) update(ql, qr, rc(p), mid + 1, r);
pushup(p);
}
inline Matrix query(int ql, int qr, int p = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr) return t[p].x;
int mid = (l + r) >> 1; Matrix res; res.make(-2);
pushdown(p);
if (ql <= mid) res = res * query(ql, qr, lc(p), l, mid);
if (qr > mid) res = res * query(ql, qr, rc(p), mid + 1, r);
return res;
}
int main() {
file("sequence");
read(n), read(m);
for (rg int i = 1; i <= n; ++i) read(a[i]);
build();
A.a[1][1] = 0, A.a[1][2] = 0, A.a[1][3] = 1;
for (rg int opt, l, r; m--; ) {
read(opt), read(l), read(r);
if (opt == 1) update(l, r);
else {
Matrix res = A * query(l, r);
printf("%d\n", (res.a[1][1] + res.a[1][2]) % mod);
}
}
return 0;
}