【学习笔记】[NOI2021] 密码箱

想起之前考试做过的一道题,当时做法好像是用线段树维护一个分段复合函数,当时切掉了,但是这道题换成矩阵就又不会了。还是太菜了。

既然都要复合了,肯定要满足结合律。因为满足了结合律,所以线段树上左右儿子可以合并。

什么东西满足结合律?自然是矩阵。这个东西用来处理线性变换尤其好用,而且矩阵的性质其实是相当丰富的,比如矩阵可以求逆。

方便起见,就用左乘好了。分数只有分子和分母两项,所以矩阵的大小为 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(){ //fixed ll x=t[rt].A.c[0][0],y=(t[rt].A.c[0][0]+t[rt].A.c[0][1])%mod; // assert(x>=0); // assert(y>=0); x=(x+mod)%mod,y=(y+mod)%mod; //assert(gcd(x,y)==1); 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{ assert(ch=='W'); 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){ assert(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){ assert(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; } } //fixed 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(); assert(p-1>=0); assert(p-1<n); 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); assert(y); assert(t[y].siz==r-l+1); flip(y);rt=merge(merge(x,y),z); assert(rt); } void Reverse(int l,int r){ int x,y,z; split(rt,l-1,x,y); split(y,r-l+1,y,z); assert(y); assert(t[y].siz==r-l+1); reverse(y);rt=merge(merge(x,y),z); assert(rt); } 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; //fixed 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(); assert(t[rt].siz==n); } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529940.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(78)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-07-05 【学习笔记】AGC010
2022-07-05 【学习笔记】zkw 线段树
点击右上角即可分享
微信分享提示