题意:
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6155
给出一个长度为n的01序列以及Q个操作,操作分两类,一种是将[L,R]内的01序列翻转,二是求出[L,R]中包含多少不同的子序列。
思路:
对于01序列,设dp[i][j]表示到前i个字符为止,末尾字符为j的子序列个种类数,可以很容易得到状态转移方程:
当str[i]==’0’时:
dp[i][0]=dp[i-1][0]+dp[i-1][1]+1;
dp[i][1]=dp[I-1][1];
当str[i]==’1’时:
dp[i][1]=dp[i-1][0]+dp[i-1][1]+1;
dp[i][0]=dp[I-1][1];
这样就可以构造矩阵,针对[L,R]的询问就转化成了求矩阵乘积的问题,因此用线段树来维护矩阵乘积,区间更新。
注意矩阵乘积满足一条性质:对参与的矩阵都进行相同的更新,那么乘积矩阵也会呈现相同的变化。
代码
#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
typedef long long LL;
const int MAXN = 1e5 + 10;
const LL MOD = 1e9 + 7;
struct mat{
LL p[3][3];
friend mat operator* (const mat& a, const mat& b) {
mat c;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
c.p[i][j] = 0;
for (int k = 0; k < 3; k++)
c.p[i][j] += a.p[i][k] * b.p[k][j] % MOD;
c.p[i][j] %= MOD;
}
return c;
}
} C[MAXN << 2];
const mat unit = {1, 0, 0, 0, 1, 0, 0, 0, 1};
const mat m[2] = {{1, 0, 0, 1, 1, 0, 1, 0, 1}, {1, 1, 0, 0, 1, 0, 0, 1, 1}};
bool lazy[MAXN << 2];
char str[MAXN];
void pushUp(int rt) {
C[rt] = C[rt << 1] * C[rt << 1 | 1];
}
void change(int rt) {
for (int i = 0; i < 3; i++) swap(C[rt].p[i][0], C[rt].p[i][1]);
for (int i = 0; i < 2; i++) swap(C[rt].p[0][i], C[rt].p[1][i]);
}
void pushDown(int rt) {
if (lazy[rt]) {
lazy[rt << 1] ^= lazy[rt];
lazy[rt << 1 | 1] ^= lazy[rt];
change(rt << 1);
change(rt << 1 | 1);
lazy[rt] = false;
}
}
void build(int l, int r, int rt) {
lazy[rt] = false;
if (l == r) {
C[rt] = m[str[l] - '0'];
return;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
pushUp(rt);
}
void update(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
lazy[rt] ^= 1;
change(rt);
return;
}
pushDown(rt);
int m = (l + r) >> 1;
if (L <= m) update(L, R, lson);
if (R > m) update(L, R, rson);
pushUp(rt);
}
mat query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) return C[rt];
int m = (l + r) >> 1;
pushDown(rt);
mat res = unit;
if (L <= m) res = res * query(L, R, lson);
if (R > m) res = res * query(L, R, rson);
return res;
}
int main() {
//freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", str + 1);
build(1, n, 1);
while (m--) {
int op, L, R;
scanf("%d%d%d", &op, &L, &R);
if (op == 1) update(L, R, 1, n, 1);
else {
mat ans = query(L, R, 1, n, 1);
printf("%I64d\n", (ans.p[2][0] + ans.p[2][1]) % MOD);
}
}
}
return 0;
}