2019-2020 ICPC, Asia Jakarta Regional Contest K题 Addition Robot线段树维护矩阵积
Posted nefu_ljw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019-2020 ICPC, Asia Jakarta Regional Contest K题 Addition Robot线段树维护矩阵积相关的知识,希望对你有一定的参考价值。
题目链接
https://codeforces.com/problemset/problem/1252/K
思路
初始a,b值写成矩阵,操作a=a+b和b=a+b也写成矩阵,则如下:
a
=
a
+
b
即
[
a
b
0
0
]
∗
[
1
0
1
1
]
=
[
a
+
b
b
0
0
]
a=a+b即\\left[ \\begin{matrix} a & b\\\\ 0 & 0 \\end{matrix} \\right] * \\left[ \\begin{matrix} 1 & 0\\\\ 1 & 1 \\end{matrix} \\right] = \\left[ \\begin{matrix} a+b & b\\\\ 0 & 0 \\end{matrix} \\right]
a=a+b即[a0b0]∗[1101]=[a+b0b0]
b = a + b 即 [ a b 0 0 ] ∗ [ 1 1 0 1 ] = [ a a + b 0 0 ] b=a+b即\\left[ \\begin{matrix} a & b\\\\ 0 & 0 \\end{matrix} \\right] * \\left[ \\begin{matrix} 1 & 1\\\\ 0 & 1 \\end{matrix} \\right] = \\left[ \\begin{matrix} a & a+b\\\\ 0 & 0 \\end{matrix} \\right] b=a+b即[a0b0]∗[1011]=[a0a+b0]
对于[1,n]区间上的每一个点维护一个矩阵,初始时若s[i]='A’则矩阵为 [ 1 0 1 1 ] \\left[\\begin{matrix}1& 0\\\\1&1\\end{matrix}\\right] [1101],若s[i]='B’则矩阵为 [ 1 1 0 1 ] \\left[\\begin{matrix}1& 1\\\\0&1\\end{matrix}\\right] [1011]。
-
对于查询操作,要查询的答案组成的矩阵即为矩阵[a,b]乘以[1,n]区间内所有矩阵的乘积。
-
对于修改操作,要使得区间内所有’A’与’B’进行互转,即将区间内矩阵乘积进行更新,容易发现 [ 1 0 1 1 ] \\left[\\begin{matrix}1& 0\\\\1&1\\end{matrix}\\right] [1101]与 [ 1 1 0 1 ] \\left[\\begin{matrix}1& 1\\\\0&1\\end{matrix}\\right] [1011]之间进行上下翻转再左右翻转即可互转。而由这两种基础矩阵取若干个相乘后的矩阵积也可同样用此方法进行翻转。
可以用线段树维护区间,区间上的每个点不再是int类型的value值,而是矩阵,这样区间维护的是这段区间内的矩阵积。
AC代码
#include <bits/stdc++.h>
#define lson i<<1
#define rson i<<1|1
using namespace std;
typedef long long ll;
const ll N=1e5+10,mod=1e9+7;
struct matrix
{
ll a[2][2];
matrix operator * (const matrix &s)
{
matrix res;
memset(res.a,0,sizeof(res.a));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
res.a[i][j]=(res.a[i][j]+a[i][k]*s.a[k][j]%mod)%mod;
return res;
}
};
matrix E={1,0,0,1}; // 2*2单位矩阵
struct node // 线段树结点
{
int l,r; // 区间范围
bool lazy; // 标记还需要翻转操作,作为懒标记
matrix m; // 需要维护更新的矩阵
}tree[N<<2];
void pushup(int i)
{
tree[i].m=tree[lson].m*tree[rson].m;
}
void build(int i,int l,int r,string s)
{
tree[i].l=l;
tree[i].r=r;
tree[i].lazy=0;
if(l==r)
{
if(s[l-1]=='A')tree[i].m={1,0,1,1}; // string下标从0开始,所以下标为l-1
else tree[i].m={1,1,0,1};
return;
}
int mid=(l+r)>>1;
build(lson,l,mid,s);
build(rson,mid+1,r,s);
pushup(i);
}
void rot(int i)
{
// 单个线段树结点中的矩阵翻转
swap(tree[i].m.a[0][0],tree[i].m.a[0][1]),swap(tree[i].m.a[1][0],tree[i].m.a[1][1]); // 左右翻转
swap(tree[i].m.a[0][0],tree[i].m.a[1][0]),swap(tree[i].m.a[0][1],tree[i].m.a[1][1]); // 上下翻转
// 更新懒标记
tree[i].lazy^=1;
}
void pushdown(int i)
{
if(tree[i].lazy) // 当前结点有标记
{
rot(lson);
rot(rson);
tree[i].lazy=0;
}
}
void update(int i,int l,int r)
{
if(tree[i].r<l||tree[i].l>r)return;
if(tree[i].l>=l&&tree[i].r<=r)return rot(i);
pushdown(i);
update(lson,l,r); // 注意不能写成[l,mid],这里的[l,r]表示要查询的区间,它是不变的
update(rson,l,r);
pushup(i);
}
matrix query(int i,int l,int r)
{
if(tree[i].r<l||tree[i].l>r)return E;
if(tree[i].l>=l&&tree[i].r<=r)return tree[i].m;
pushdown(i);
return query(lson,l,r)*query(rson,l,r);
}
int main()
{
ios::sync_with_stdio(false);
int n,q,opt,l,r,a,b;
string str;
cin>>n>>q>>str;
build(1,1,n,str);
while(q--)
{
cin>>opt;
if(opt==1)
{
cin>>l>>r;
update(1,l,r);
}
else
{
cin>>l>>r>>a>>b;
matrix s1={a,b,0,0};
matrix s2=query(1,l,r); // [l,r]区间内的矩阵积
matrix ans=s1*s2;
printf("%lld %lld\\n",ans.a[0][0],ans.a[0][1]);
}
}
return 0;
}
/*
5 3
ABAAA
2 1 5 1 1
1 1 5
2 1 5 1 1
ans:
3 11
以上是关于2019-2020 ICPC, Asia Jakarta Regional Contest K题 Addition Robot线段树维护矩阵积的主要内容,如果未能解决你的问题,请参考以下文章
2019-2020 ICPC, Asia Jakarta Regional Contest H. Twin Buildings
比赛:ICPC Asia Taipei-Hsinchu Regional 2019 2020.4.1
2019-2020 ICPC Asia Taipei-Hsinchu Regional Contest 训练记录
2019-2020 ICPC, Asia Jakarta Regional Contest.E. Songwriter
2019-2020 ICPC, Asia Jakarta Regional Contest K题 Addition Robot线段树维护矩阵积