CF1252K Addition Robot 题解

题目链接

思路

对于 A=A+BB=A+B 这种的递推式可以考虑矩阵加速递推,可得:

(A+BB)=(AB)(1011)

(AA+B)=(AB)(1101)

A,B 二元组看作单位向量 (AB),设 Si 对应的转移矩阵为 Gi,则一次 F 函数操作得到的 A,B 二元组对应的向量就是:

(AB)i=lrGi

由于矩阵乘法满足结合律,对于一段区间的矩阵的乘积,我们可以用线段树维护。

考虑区间取反操作怎么实现,我们通过打表观察上方给出的两个转移矩阵,发现它们都是对方旋转了 180。感性理解一下,设有矩阵 A,B,C,有:

A=(a1b1c1d1)

B=(a2b2c2d2)

C=A×B=(a1a2+b1c2a1b2+b1d2c1a2+d1c2c1b2+d1d2)

ABAB 各旋转 180 后的矩阵,有:

A=(d1c1b1a1)

B=(d2c2b2a2)

C=A×B=(d1d2+c1b2d1c2+c1a2b1d2+a1b2b1c2+a1a2)

可以发现 C 也是 C 旋转 180 后的结果,所以推广这个结论到 A,B 的反转操作上,就是直接交换矩阵对角线上的元素。就像维护01序列的翻转那样维护线段树就行了。时间复杂度 Θ(23nlogn)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int Mod = 1e9 + 7;
const int MAXN = 1e5 + 10, SIZE = 3;
int n, q;
char s[MAXN];
struct Matrix{
int hig, wid;
LL num[SIZE][SIZE];
Matrix(){
hig = wid = 0;
memset(num, 0, sizeof(num));
}
Matrix operator * (const Matrix &b) const{
Matrix c;
c.hig = hig, c.wid = b.wid;
for(register int i = 1; i <= c.hig; i++){
for(register int k = 1; k <= b.hig; k++){
LL res = num[i][k];
for(register int j = 1; j <= c.wid; j++)
c.num[i][j] = 1LL * (c.num[i][j] + res * b.num[k][j] % Mod) % Mod;
}
}
return c;
}
void Reverse(){
swap(num[1][1], num[2][2]);
swap(num[1][2], num[2][1]);
}
}base, G1, G2, I;
struct Segment_Tree{
struct Tree{
int l, r;
Matrix sum;
int lazy;
}tr[MAXN << 2];
inline int lson(int rt){
return rt << 1;
}
inline int rson(int rt){
return rt << 1 | 1;
}
inline void Pushup(int rt){
tr[rt].sum = tr[lson(rt)].sum * tr[rson(rt)].sum;
}
void Build(int rt, int l, int r){
tr[rt].l = l, tr[rt].r = r;
if(l == r){
if(s[l] == 'A') tr[rt].sum = G1;
else tr[rt].sum = G2;
return;
}
int mid = (l + r) >> 1;
Build(lson(rt), l, mid);
Build(rson(rt), mid + 1, r);
Pushup(rt);
}
inline void Pushdwon(int rt){
if(tr[rt].lazy){
tr[lson(rt)].lazy ^= 1;
tr[rson(rt)].lazy ^= 1;
tr[lson(rt)].sum.Reverse();
tr[rson(rt)].sum.Reverse();
tr[rt].lazy = 0;
}
}
void Update_Rev(int rt, int l, int r){
if(tr[rt].l >= l && tr[rt].r <= r){
tr[rt].sum.Reverse();
tr[rt].lazy ^= 1;
return;
}
Pushdwon(rt);
int mid = (tr[rt].l + tr[rt].r) >> 1;
if(l <= mid) Update_Rev(lson(rt), l, r);
if(r > mid) Update_Rev(rson(rt), l, r);
Pushup(rt);
}
Matrix Query_Sum(int rt, int l, int r){
if(tr[rt].l >= l && tr[rt].r <= r) return tr[rt].sum;
Pushdwon(rt);
Matrix ans = base;
int mid = (tr[rt].l + tr[rt].r) >> 1;
if(l <= mid) ans = ans * Query_Sum(lson(rt), l, r);
if(r > mid) ans = ans * Query_Sum(rson(rt), l, r);
return ans;
}
}S;
void init(){
base.hig = base.wid = 2;
base.num[1][1] = base.num[2][2] = 1;
G1.hig = G1.wid = 2;
G1.num[1][1] = G1.num[2][1] = G1.num[2][2] = 1;
G2.hig = G2.wid = 2;
G2.num[1][1] = G2.num[1][2] = G2.num[2][2] = 1;
I.hig = 1, I.wid = 2;
}
int main(){
init();
scanf("%d%d", &n, &q);
scanf("%s", s + 1);
S.Build(1, 1, n);
for(register int i = 1; i <= q; i++){
int opt;
scanf("%d", &opt);
if(opt == 1){
int l, r;
scanf("%d%d", &l, &r);
S.Update_Rev(1, l, r);
}
else{
int l, r, a, b;
scanf("%d%d%d%d", &l, &r, &a, &b);
I.num[1][1] = a, I.num[1][2] = b;
I = I * S.Query_Sum(1, l, r);
printf("%lld %lld\n", I.num[1][1], I.num[1][2]);
}
}
return 0;
}

马蜂冗长,不喜勿喷。

posted @   TSTYFST  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示