线段树维护矩阵乘法

  • 2019 年 11 月 25 日
  • 筆記

线段树维护矩阵乘法

2019-2020 ICPC, Asia Jakarta Regional Contest

K Addition Robot

题目

Adding two numbers several times is a time-consuming task, so you want to build a robot. The robot should have a string of characters on its memory that represents addition instructions. Each character of the string, , is either or.

You want to be able to give commands to the robot, each command is either of the following types:

  • 1 . The robot should toggle all the characters of where . Toggling a character means changing it to if it was previously , or changing it to if it was previously .
  • 2 . The robot should call and return two integers as defined in the following pseudocode:
    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)

You want to implement the robot's expected behavior.

Input

Input begins with a line containing two integers: () representing the number of characters in the robot's memory and the number of commands, respectively. The next line contains a string containing characters (each either or ) representing the initial string in the robot's memory. The next lines each contains a command of the following types.

  • 1
  • 2

There is at least one command of the second type.

output

For each command of the second type in the same order as input, output in a line two integers (separated by a single space), the value of and returned by , respectively. As this output can be large, you need to modulo the output by .

Example

input

5 3  ABAAA  2 1 5 1 1  1 3 5  2 2 5 0 1000000000
output
11 3  0 1000000000

翻译

给一个长度为的,字符串,给出次询问。

  • 操作1 输入, 将区间,的变为,变为;
  • 操作2 输入, , ,,在进行遍历,若遇到则,若遇到 则,输出最后,的值

如样例第一个询问2 1 5 1 1则在区间[1,5]上遍历,初始值,。

第一个遇到,所以,;

第二个遇到,所以,;

第三个遇到,所以 ,;

第四个遇到,所以,;

第五个遇到,所以,;

输出答案,;

题解

先不考虑操作1

注意到两个矩阵

这两个操作像不像遇到和时候的两种操作?至于为什么要这样构造矩阵,只能说这样构造可以解决,可以当成一种套路,这种线性的递推变换都可以通过构造一个转移矩阵得到解决。

然后对于一个区间, 遇到我们就乘矩阵(1),遇到B我们就乘一个矩阵(2)对于样例[1,5]区间来说

而矩阵是满足结合律的可以将即

所以我们需要做的只是求出某个询问区间[L,R]的矩阵的积,这显然是可以用线段树做到的。

再考虑操作1,将区间中的变成,变成

之前讲到用线段树去查询区间的矩阵积,这里带了一种修改,但是一个区间,最多只会有两种情况,要么被交换了和要么没有被交换,所以在用线段树维护的时候将两个情况都维护一下,如果要进行交换,则直接交换维护的两个值即可。因为是区间修改,所以用打一下标记。

最近的一场关于印尼Regional镜像赛,codefoces上可补题

最后推荐看懂的同学自己写一下,因为太菜所以觉得细节还是蛮多的hhhh。

代码

#include <bits/stdc++.h>  using namespace std;  typedef long long ll;  typedef pair<int,int> pii;  #define mp make_pair  const int maxn = 1e5+5;  const int MOD = 1e9+7;  #define mod(x) x % MOD  #define INF 0x3f3f3f3f    char a[maxn];  struct mat{      ll m[3][3];  }unit;  mat mul(mat a, mat b, int sz) {      mat x;      for(int i = 0;i < sz;i++) {          for(int j = 0;j < sz;j++) {              ll res = 0;              for(int k = 0;k < sz;k++) {                  res += a.m[i][k] * b.m[k][j];                  res = mod(res);              }              x.m[i][j] = mod(res);          }      }      return x;  }  struct node {      int l, r;      int mid() {          return (l + r) / 2;      }      mat ans, rans;      int lazy;  } tree[maxn << 2];  void pushUp(int rt) {      tree[rt].ans = mul(tree[rt << 1].ans, tree[rt << 1 | 1].ans, 2);      tree[rt].rans = mul(tree[rt << 1].rans, tree[rt << 1 | 1].rans, 2);  }  void pushDown(int rt) {      if(tree[rt].lazy) {          swap(tree[rt << 1].ans, tree[rt << 1].rans);          swap(tree[rt << 1 | 1].ans, tree[rt << 1 | 1].rans);          tree[rt << 1].lazy ^= 1;          tree[rt << 1 | 1].lazy ^= 1;          tree[rt].lazy = 0;      }  }  void build(int rt, int l, int r) {      tree[rt].l = l, tree[rt].r = r;      tree[rt].lazy = 0;      if(l == r){          if(a[l] == 'A') {              tree[rt].ans.m[0][0] = 1, tree[rt].ans.m[0][1] = 0;              tree[rt].ans.m[1][0] = 1, tree[rt].ans.m[1][1] = 1;              tree[rt].rans.m[0][0] = 1, tree[rt].rans.m[0][1] = 1;              tree[rt].rans.m[1][0] = 0, tree[rt].rans.m[1][1] = 1;          }          if(a[l] == 'B') {              tree[rt].ans.m[0][0] = 1, tree[rt].ans.m[0][1] = 1;              tree[rt].ans.m[1][0] = 0, tree[rt].ans.m[1][1] = 1;              tree[rt].rans.m[0][0] = 1, tree[rt].rans.m[0][1] = 0;              tree[rt].rans.m[1][0] = 1, tree[rt].rans.m[1][1] = 1;          }          return;      }      int m = tree[rt].mid();      build(rt << 1, l, m);      build(rt << 1 | 1, m + 1, r);      pushUp(rt);  }  void update(int rt, int l, int r) {      if(tree[rt].l >= l && tree[rt].r <= r) {          tree[rt].lazy ^= 1;          swap(tree[rt].ans, tree[rt].rans);          return;      }      pushDown(rt);      int m = tree[rt].mid();      if(r <= m) update(rt << 1, l, r);      else if(l > m) update(rt << 1 | 1, l, r);      else {          update(rt << 1, l, m);          update(rt << 1 | 1, m + 1, r);      }      pushUp(rt);  }  mat query(int rt, int l, int r) {      if(tree[rt].l >= l && tree[rt].r <= r) {          return tree[rt].ans;      }      pushDown(rt);      int m = tree[rt].mid();      mat res;      if(r <= m) {          res = query(rt << 1, l, r);      }      else if(l > m) {          res = query(rt << 1 | 1, l, r);      }      else {          res = query(rt << 1, l, m);          res = mul(res, query(rt << 1 | 1, m + 1, r), 2);      }      pushUp(rt);      return res;  }    int main(){      int n = 0, q = 0;      scanf("%d %d", &n, &q);      scanf("%s", a + 1);      build(1, 1, n);      mat A, B;      B.m[0][0] = 1, B.m[0][1] = 1;      B.m[1][0] = 0, B.m[1][1] = 1;      A.m[0][0] = 1, A.m[0][1] = 0;      A.m[1][0] = 1, A.m[1][1] = 1;      while(q--) {          int k = 0;          scanf("%d", &k);          if(k == 1) {              int l, r;              scanf("%d %d", &l, &r);              update(1, l, r);          }          else {              int l, r;              ll a, b;              scanf("%d %d %lld %lld", &l, &r, &a, &b);              mat C;              C.m[0][0] = a, C.m[0][1] = b;              mat tmp = query(1, l, r);              C = mul(C, tmp, 2);              printf("%lld %lldn", mod(C.m[0][0]), mod(C.m[0][1]) );          }      }      return 0;  }