[CF1252K] Addition Robot 题解
[CF1252K] Addition Robot 题解
矩阵
对于操作二而言,我们可以将其转换成矩阵的形式,为了避免混淆,以下将原题中的函数参数 A
B
替代为 a
b
。
即如果当前是位置 A
,执行
\[\begin{pmatrix}
b & a
\end{pmatrix}
·
\begin{pmatrix}
1 & 1\\
0 & 1
\end{pmatrix}
=
\begin{pmatrix}
b & a + b
\end{pmatrix}
\]
同理可得,如果当前是位置 B
,执行
\[\begin{pmatrix}
b & a
\end{pmatrix}
·
\begin{pmatrix}
1 & 0\\
1 & 1
\end{pmatrix}
=
\begin{pmatrix}
b + a & a
\end{pmatrix}
\]
称矩阵
\[\begin{pmatrix}
1 & 1\\
0 & 1
\end{pmatrix}
\]
为 \(\alpha\) 矩阵。
矩阵
\[\begin{pmatrix}
1 & 0\\
1 & 1
\end{pmatrix}
\]
为 \(\beta\) 矩阵。
若将所有为 A
的位置上放上 \(\alpha\) 矩阵,所有为 B
的位置上放上 \(\beta\) 矩阵,则操作二可看作是矩阵
\[\begin{pmatrix}
b & a
\end{pmatrix}
\]
乘上区间 \([l, r]\) 中的矩阵乘积。
可以用线段树维护这个东西。
线段树
考虑如何快速处理区间翻转后的矩阵,手模规律可以发现,区间翻转后的矩阵就变为了原矩阵对角线互换。
具体而言,对于一个线段树节点维护的矩阵
\[\begin{pmatrix}
a & b\\
c & d
\end{pmatrix}
\]
它维护的区间翻转后的矩阵成绩必然为
\[\begin{pmatrix}
d & c\\
b & a
\end{pmatrix}
\]
因此加上懒标记即可完成这题
时间复杂度:\(O(C m\log n)\),其中 \(C = 2^3\),是矩阵乘法所需的常数。
// Problem: Addition Robot
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1252K
// Memory Limit: 250 MB
// Time Limit: 3000 ms
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-03-08 23:03:12
#include <cstring>
#include <iostream>
#define int long long
#define speedup (ios::sync_with_stdio(0), cin.tie(0), cout.tie(0))
using namespace std;
const int N = 1e5 + 10, mod = 1e9 + 7;
struct mat
{
int m[3][3];
int r, c;
void clear(int R, int C)
{
memset(m, 0, sizeof m);
r = R, c = C;
}
void init()
{
for(int i = 1; i <= min(r, c); i ++) m[i][i] = 1;
}
friend mat operator * (const mat a, const mat b)
{
mat res;
res.clear(a.r, b.c);
for(int i = 1; i <= a.r; i ++)
for(int j = 1; j <= b.c; j ++)
for(int k = 1; k <= a.c; k ++)
res.m[i][j] = (a.m[i][k] * b.m[k][j] % mod + res.m[i][j]) % mod;
return res;
}
void operator = (const mat a)
{
for(int i = 1; i <= a.r; i ++)
for(int j = 1; j <= a.c; j ++)
m[i][j] = a.m[i][j];
}
void print()
{
for(int i = 1; i <= r; i ++, cout << endl)
for(int j = 1; j <= c; j ++)
cout << m[i][j] << ' ';
}
void change()
{
swap(m[1][1], m[2][2]);
swap(m[1][2], m[2][1]);
}
} ;
struct qwq
{
int l, r;
mat m;
bool tag;
} tr[N << 2];
mat alpha, beta;
bool ini[N];
void up(int k)
{
tr[k].m = tr[k << 1].m * tr[k << 1 | 1].m;
}
void down(int k)
{
if(!tr[k].tag) return ;
tr[k << 1].m.change(), tr[k << 1 | 1].m.change();
tr[k << 1].tag ^= 1, tr[k << 1 | 1].tag ^= 1;
tr[k].tag = 0;
}
void build(int k, int l, int r)
{
int mid = l + r >> 1;
tr[k] = {l, r};
tr[k].m.clear(2, 2), tr[k].m.init();
if(l == r) tr[k].m = (ini[l] ? beta : alpha);
else build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r), up(k);
// cout << k << ' ' << l << ' ' << r << endl;
// tr[k].m.print();
}
void update(int k, int ql, int qr)
{
int l = tr[k].l, r = tr[k].r, mid = l + r >> 1;
if(ql <= l && qr >= r)
{
tr[k].m.change();
tr[k].tag ^= 1;
return ;
}
down(k);
if(ql <= mid) update(k << 1, ql, qr);
if(qr > mid) update(k << 1 | 1, ql, qr);
up(k);
}
mat query(int k, int ql, int qr)
{
int l = tr[k].l, r = tr[k].r, mid = l + r >> 1;
if(ql <= l && qr >= r)
return tr[k].m;
down(k);
mat tmp;
tmp.clear(2, 2), tmp.init();
if(ql <= mid) tmp = tmp * query(k << 1, ql, qr);
if(qr > mid) tmp = tmp * query(k << 1 | 1, ql, qr);
return tmp;
}
signed main()
{
speedup;
int n, q;
cin >> n >> q;
alpha.clear(2, 2);
alpha.m[1][1] = alpha.m[1][2] = alpha.m[2][2] = 1;
beta.clear(2, 2);
beta.m[1][1] = beta.m[2][1] = beta.m[2][2] = 1;
string s;
cin >> s;
for(int i = 1; i <= n; i ++)
{
if(s[i - 1] == 'A') ini[i] = 0;
else ini[i] = 1;
}
build(1, 1, n);
mat A;
A.clear(1, 2);
while(q --)
{
int op;
cin >> op;
if(op == 1)
{
int l, r;
cin >> l >> r;
update(1, l, r);
}
else
{
int l, r, a, b;
cin >> l >> r >> a >> b;
A.m[1][2] = a, A.m[1][1] = b;
A = (A * query(1, l, r));
cout << A.m[1][2] << ' ' << A.m[1][1] << "\n";
}
}
return 0;
}