想起之前考试做过的一道题,当时做法好像是用线段树维护一个分段复合函数,当时切掉了,但是这道题换成矩阵就又不会了。还是太菜了。
既然都要复合了,肯定要满足结合律。因为满足了结合律,所以线段树上左右儿子可以合并。
什么东西满足结合律?自然是矩阵。这个东西用来处理线性变换尤其好用,而且矩阵的性质其实是相当丰富的,比如矩阵可以求逆。
方便起见,就用左乘好了。分数只有分子和分母两项,所以矩阵的大小为 2 × 2 2\times 2 2×2即可。那么我们有 [ x y ] × [ a i 1 1 0 ] = [ a i x + y x ] \begin{bmatrix}x\ y\end{bmatrix}\times \begin{bmatrix}a_i\ 1\\1\ \ 0\end{bmatrix}=\begin{bmatrix}a_ix+y\ \ x\end{bmatrix} [x y]×[ai 11 0]=[aix+y x]。大胆猜测 W W W, E E E两种操作都分别对应一种矩阵,具体来说:
1.1
1.1
1.1
W
W
W操作对应左乘矩阵
[
1
1
0
1
]
\begin{bmatrix}1\ 1\\0\ 1\end{bmatrix}
[1 10 1]。
1.2
1.2
1.2
E
E
E 操作对应两种情况,但是有一个结论:两种情况对应同一个矩阵。第二种情况对应的矩阵是
[
1
1
1
0
]
×
[
1
1
1
0
]
×
[
1
−
1
0
1
]
=
[
2
−
1
1
0
]
\begin{bmatrix}1\ 1\\1 \ 0\end{bmatrix}\times \begin{bmatrix}1\ 1\\1\ 0\end{bmatrix}\times \begin{bmatrix}1\ -1\\0\ \ \ \ 1\end{bmatrix}=\begin{bmatrix}2\ -1\\1\ \ \ \ 0\end{bmatrix}
[1 11 0]×[1 11 0]×[1 −10 1]=[2 −11 0];第一种情况对应的矩阵是
[
1
1
1
0
]
×
[
1
1
0
1
]
×
[
1
1
1
0
]
−
1
=
[
2
−
1
1
0
]
\begin{bmatrix}1\ 1\\1\ 0\end{bmatrix}\times \begin{bmatrix}1\ 1\\0\ 1\end{bmatrix}\times \begin{bmatrix}1\ 1\\1\ 0\end{bmatrix}^{-1}=\begin{bmatrix}2\ -1\\1\ \ \ \ 0\end{bmatrix}
[1 11 0]×[1 10 1]×[1 11 0]−1=[2 −11 0]。不难发现这些巧合事实上都是出题人的精心设计。
然后把线段树换成平衡树,这道题就做完了。
如果正常来做的话,那么一个节点就要维护 4 4 4个矩阵,这样常数就会有点大。但是因为两个标记的先后顺序不影响答案所以代码难度也还好。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
写了一发,感受是甚至比普通的平衡树要好写。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
const int N=2e5+5;
struct Matrix{
ll c[2][2];
Matrix(){memset(c,0,sizeof c);}
Matrix operator *(const Matrix &a)const{
Matrix r;
for(int k=0;k<2;k++){
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
}
}
}
return r;
}
}W,E,I;
struct node{
Matrix A,B,Av,Bv;
Matrix v,rv;
int tagflip,tagrev;
int l,r,siz,fix;
}t[N];
int n,Q,rt;
string str;
int gcd(int x,int y){
return y==0?x:gcd(y,x%y);
}
void getans(){
ll x=t[rt].A.c[0][0],y=(t[rt].A.c[0][0]+t[rt].A.c[0][1])%mod;
x=(x+mod)%mod,y=(y+mod)%mod;
cout<<x<<" "<<y<<"\n";
}
int newnode(char ch){
n++;t[n].siz=1,t[n].fix=rand();
if(ch=='E'){
t[n].A=t[n].Av=t[n].v=E;
t[n].B=t[n].Bv=t[n].rv=W;
}
else{
t[n].A=t[n].Av=t[n].v=W;
t[n].B=t[n].Bv=t[n].rv=E;
}
return n;
}
//fixed
void pushup(int p){
int l=t[p].l,r=t[p].r;
t[p].siz=t[l].siz+t[r].siz+1;
t[p].A=t[r].A*t[p].v*t[l].A;
t[p].Av=t[l].Av*t[p].v*t[r].Av;
t[p].B=t[r].B*t[p].rv*t[l].B;
t[p].Bv=t[l].Bv*t[p].rv*t[r].Bv;
}
//fixed
void flip(int p){
t[p].tagflip^=1;
swap(t[p].A,t[p].B),swap(t[p].Av,t[p].Bv),swap(t[p].v,t[p].rv);
}
void reverse(int p){
t[p].tagrev^=1,swap(t[p].l,t[p].r);
swap(t[p].A,t[p].Av),swap(t[p].B,t[p].Bv);
}
void pushdown(int p){
int l=t[p].l,r=t[p].r;
if(t[p].tagflip){
if(l)flip(l);
if(r)flip(r);
t[p].tagflip=0;
}
if(t[p].tagrev){
if(l)reverse(l);
if(r)reverse(r);
t[p].tagrev=0;
}
}
//fixed
int merge(int x,int y){
if(!x||!y)return x+y;
if(t[x].fix>t[y].fix){
pushdown(x);
t[x].r=merge(t[x].r,y);
pushup(x);
return x;
}
else{
pushdown(y);
t[y].l=merge(x,t[y].l);
pushup(y);
return y;
}
}
void split(int rt,int val,int &x,int &y){
if(!rt){
x=y=0;
return;
}
pushdown(rt);
int l=t[rt].l,r=t[rt].r;
if(t[l].siz>=val){
y=rt;
split(l,val,x,t[y].l);
pushup(y);
}
else{
x=rt;
split(r,val-t[l].siz-1,t[x].r,y);
pushup(x);
}
}
void build(int &p,int l,int r){
if(l>r)return;
int mid=l+r>>1;p=mid;
t[p].fix=rand();
if(str[p-1]=='E'){
t[p].v=E;
t[p].rv=W;
}
else{
t[p].v=W;
t[p].rv=E;
}
build(t[p].l,l,mid-1);
build(t[p].r,mid+1,r);
pushup(p);
}
void Flip(int l,int r){
int x,y,z;
split(rt,l-1,x,y);
split(y,r-l+1,y,z);
flip(y);rt=merge(merge(x,y),z);
}
void Reverse(int l,int r){
int x,y,z;
split(rt,l-1,x,y);
split(y,r-l+1,y,z);
reverse(y);rt=merge(merge(x,y),z);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>Q>>str;srand(time(0));
W.c[0][0]=1,W.c[0][1]=1,W.c[1][0]=0,W.c[1][1]=1;
E.c[0][0]=2,E.c[0][1]=-1,E.c[1][0]=1,E.c[1][1]=0;
I.c[0][0]=I.c[1][1]=1;
t[0].A=t[0].B=t[0].Av=t[0].Bv=I;
build(rt,1,n);getans();
for(int i=1;i<=Q;i++){
cin>>str;
if(str[0]=='A'){
char ch;cin>>ch;
rt=merge(rt,newnode(ch));
}
else if(str[0]=='F'){
int l,r;cin>>l>>r;
Flip(l,r);
}
else if(str[0]=='R'){
int l,r;cin>>l>>r;
Reverse(l,r);
}
getans();
}
}