线段树维护矩阵乘法

题目

题意

1
2
3
4
5
6
7
function f(L, R, A, B):
      FOR i from L to R
        if S[i] = 'A'
          A = A + B
        else
          B = A + B
      return (A, B)

问给定字符串 S 和范围 [L,R] 和AB初值,运行上述代码后的A,B取值。

还有区间翻转操作,将A该成B,B改成A。

题解

  • 考虑化成矩阵乘法,线段树维护区间,线段树维护矩阵乘法即可。

  • 翻转时可以手动算几个字符串,可以发现翻转操作就是把矩阵的两个对角交换。可以完成修改操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define LL long long
using namespace std;
const int maxn = 1e5+5;
const LL mod = 1e9+7;
 
struct Mat{
    LL m[2][2];
    Mat operator + (const Mat & a) const{
        Mat sum; memset(sum.m, 0, sizeof sum.m);
        for(int i=0; i<2; i++) for(int j=0; j<2; j++)
            for(int k=0; k<2; k++)
                sum.m[i][k] = (sum.m[i][k] + m[i][j] * a.m[j][k] % mod)%mod;
        return sum;
    }
    void Reserve(){
        swap(m[0][0], m[1][1]);
        swap(m[1][0], m[0][1]);
    }
}sum[maxn<<2], a[maxn], A, B, One;
int tag[maxn<<2];
void pushdown(int rt){
    if(tag[rt]){
        tag[ls] ^= tag[rt]; tag[rs] ^= tag[rt];
        sum[ls].Reserve(); sum[rs].Reserve();
        tag[rt] = 0;
    }
}
void build(int rt, int l, int r){
    if(l == r){
        sum[rt] = a[l];
        return ;
    }int mid = l+r >> 1;
    build(ls, l, mid); build(rs, mid+1, r);
    sum[rt] = sum[ls] + sum[rs];
}
void Upd(int rt, int l, int r, int L, int R){
    if(L <= l && R >= r) {
        sum[rt].Reserve();
        tag[rt] ^= 1;
        return ;
    }
    pushdown(rt);
    int mid = l+r >> 1;
    if(L <= mid) Upd(ls, l, mid, L, R);
    if(R > mid) Upd(rs, mid+1, r, L, R);
    sum[rt] = sum[ls] + sum[rs];
}
Mat Qry(int rt, int l, int r, int L, int R){
    if(L <= l && R >= r) return sum[rt];
    pushdown(rt);
    int mid = l+r >> 1; Mat res = One;
    if(L <= mid) res = res + Qry(ls, l, mid, L, R);
    if(R > mid) res = res + Qry(rs, mid+1, r, L, R);
    return res;
}
void print(Mat a){
    puts("***");
    for(int i=0; i<2; i++) 
        for(int j=0; j<2; j++)
            printf("%d ", a.m[i][j]);
    cout << endl;
}
int n, m;
char s[maxn];
int main(){
    One.m[0][0] = One.m[1][1] = 1;
    A.m[0][0] = A.m[1][0] = A.m[1][1] = 1; A.m[0][1] = 0;
    B.m[0][0] = B.m[0][1] = B.m[1][1] = 1; B.m[1][0] = 0;
    scanf("%d%d%s", &n, &m, s);
    for(int i=0; i<n; i++){
        if(s[i] == 'A') a[i+1] = A;
        else a[i+1] = B;
    }
    build(1, 1, n);
    LL op, l, r, x, y;
    for(int i=1; i<=m; i++){
        scanf("%lld%lld%lld", &op, &l, &r);
        if(op == 1){
            Upd(1, 1, n, l, r);
        }else {
            scanf("%lld%lld", &x, &y);
            Mat ret; ret.m[0][0] = x; ret.m[0][1] = y; ret.m[1][0] = ret.m[1][1] = 0;
            Mat ans = Qry(1, 1, n, l, r);
            ret = ret + ans;
            printf("%lld %lld\n", ret.m[0][0], ret.m[0][1]);
        }
    }
}
# 使用单$作为行内数学公式分界符