P7739-[NOI2021]密码箱【Splay,矩阵乘法】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P7739


1|1题目描述

懒得概括,摸了。

Yelekastee 是 U 国著名的考古学家。在最近的一次考古行动中,他发掘出了一个远古时期的密码箱。经过周密而严谨的考证,Yelekastee 得知密码箱的密码和某一个数列 {an} 相关。数列 {an} 可以用如下方式构造出来:

  1. 初始时数列长度为 2 且有 a0=0,a1=1
  2. 对数列依次进行若干次操作,其中每次操作是以下两种类型之一:
  • W 类型:给数列的最后一项1
  • E 类型:若数列的最后一项1,则给倒数第二项加 1;否则先给数列的最后一项1,接着在数列尾再加两项,两项的值都是 1

受到技术限制,密码箱并没有办法完整检查整个数列,因此密码箱的密码设定为数列 {an} 经过函数 f 作用后的值,其中 f 的定义如下:

f(a0,,ak1,ak)={a0,amp;k=0f(a0,a1,,ak2,ak1+1ak),amp;k1

Yelekastee 并不擅长运算,因此他找到了你,希望你能根据他提供的操作序列计算出密码箱的密码。不幸的是,他的记性并不是很好,因此他会随时对提供的操作序列做出一些修改,这些修改包括以下三种:

  • APPEND c,在现有操作序列后追加一次 c 类型操作,其中 c 为字符 WE
  • FLIP l r,反转现有操作序列中第 l 个至第 r 个(下标从 1 开始,修改包含端点 lr,下同)操作,即所有 W 变为 E,所有 E 变为 W
  • REVERSE l r,翻转现有操作序列中第 l 个至第 r 个操作,也就是将这个区间中的操作逆序。

1n,q105


1|2解题思路

先考虑知道a序列怎么求答案,假设上一个传下来的是xy,那么新的那个就是xy=ak+xy=yak+xx,那么有x=yak+x,y=x

显然如果我们只是动态修改每一个a,那么上面这个转移可以用矩阵乘法维护。

但是我们现在的操作可能是让一些数+1和在数列后面+1。麻烦的是让一些数+1这个操作,我们考虑如果我们在末尾加入了一个1,那么转移x=x+y,y=x,如果让末尾的一个数加1,那么x=x+y,y=y。这样就可以直接考虑矩阵维护EW操作了,我们分别设上面两个操作的矩阵为HA,HB,我们求出一个GA×HA=1,GB×HB=1这样就可以取消掉前面的一些操作了。

然后考虑一下下面的操作怎么实现,设目前的矩阵乘积为M

  1. 让末尾+1M=HA×HB×GA×M
  2. 让倒数第二个+1:注意到此时最后一个数字肯定是1,所以M=HA×HA×HB×GA×GA×M
  3. 让最后一个数1,然后在末尾加入两个1M=HA×HA×HA×GB×GA×M

然后我们会愉快的发现后两个和E有关的操作有HA×HA×HB×GA×GA=HA×HA×HA×GB×GA

也就是其实E两种情况的操作乘上的都是同一个矩阵,那么用Splay维护即可,区间翻转和区间取反操作可以直接维护四个值,表示是否取反/翻转后的矩阵就行了。

至于输出x%P,y%Pgcd(x,y)1的要求我们不用担心,因为上面的操作中x,ygcd都是不会变的(因为gcd(x+y,x)=gcd(x,y)),所以无论怎样操作x,y都是互质的。

时间复杂度:O((n+q)logn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespace std; const int N=2e5+10,S=2,P=998244353; int read(){ int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-f;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f; } void print(int x) {if(x>9)print(x/10);putchar('0'+x%10);} struct Matrix{ int a[S][S]; }Ha,Hb,Ga,Gb,HA,HB,HC,O,c; void add(int &x,int y) {x=(x+y>=P)?(x+y-P):(x+y);} Matrix operator*(const Matrix &a,const Matrix &b){ memset(c.a,0,sizeof(c.a)); for(int i=0;i<S;i++) for(int j=0;j<S;j++) for(int k=0;k<S;k++) add(c.a[i][j],1ll*a.a[i][k]*b.a[k][j]%P); return c; } struct node{ Matrix M,N; void flp(){swap(M,N);return;} }W,E,tmp; int n,q,cnt,root; char s[N]; struct PeTree{ node w[N],v[N],a[N]; int t[N][2],fa[N],siz[N]; bool r[N],u[N]; void PushUp(int x){ w[x]=v[x]=a[x]; if(t[x][1]){ w[x].M=w[t[x][1]].M*w[x].M; v[x].M=v[x].M*v[t[x][1]].M; w[x].N=w[t[x][1]].N*w[x].N; v[x].N=v[x].N*v[t[x][1]].N; } if(t[x][0]){ w[x].M=w[x].M*w[t[x][0]].M; v[x].M=v[t[x][0]].M*v[x].M; w[x].N=w[x].N*w[t[x][0]].N; v[x].N=v[t[x][0]].N*v[x].N; } siz[x]=siz[t[x][0]]+siz[t[x][1]]+1; return; } void PushR(int x) {swap(t[x][0],t[x][1]);swap(w[x],v[x]);r[x]^=1;return;} void PushU(int x) {w[x].flp();v[x].flp();a[x].flp();u[x]^=1;return;} void PushDown(int x){ if(r[x])PushR(t[x][0]),PushR(t[x][1]),r[x]=0; if(u[x])PushU(t[x][0]),PushU(t[x][1]),u[x]=0; return; } bool Direct(int x){return t[fa[x]][1]==x;} void Rotate(int x){ int y=fa[x],z=fa[y]; int xs=Direct(x),ys=Direct(y); int w=t[x][xs^1]; if(z)t[z][ys]=x; t[x][xs^1]=y;t[y][xs]=w; if(w)fa[w]=y;fa[y]=x;fa[x]=z; PushUp(y);PushUp(x);return; } void Downdata(int x,int f){ if(x==f)return; Downdata(fa[x],f); PushDown(x); return; } void Splay(int x,int f){ if(!f)root=x; Downdata(x,f); while(fa[x]!=f){ int y=fa[x]; if(fa[y]==f)Rotate(x); else if(Direct(x)==Direct(y)) Rotate(y),Rotate(x); else Rotate(x),Rotate(x); } return; } int Find(int val,int x=root){ PushDown(x); if(siz[t[x][0]]>=val)return Find(val,t[x][0]); if(siz[t[x][0]]+1==val)return x; return Find(val-siz[t[x][0]]-1,t[x][1]); } }T; int power(int x,int b){ int ans=1; while(b){ if(b&1)ans=1ll*ans*x%P; x=1ll*x*x%P;b>>=1; } return ans; } void Query(){ T.Splay(1,0); T.Splay(n+2,1); Matrix ans=T.w[T.t[n+2][0]].M; int a=ans.a[0][0]; int b=ans.a[0][1]; swap(a,b);a+=b; a=(a%P+P)%P;b=(b+P)%P; print(b);putchar(' '); print(a);putchar('\n'); } signed main() { n=read();q=read(); O.a[0][0]=1;O.a[1][0]=0; O.a[1][0]=0;O.a[1][1]=1; Ha.a[0][0]=1;Ha.a[1][0]=1; Ha.a[0][1]=0;Ha.a[1][1]=1; Ga.a[0][0]=1;Ga.a[1][0]=P-1; Ga.a[0][1]=0;Ga.a[1][1]=1; Hb.a[0][0]=1;Hb.a[1][0]=1; Hb.a[0][1]=1;Hb.a[1][1]=0; Gb.a[0][0]=0;Gb.a[1][0]=1; Gb.a[0][1]=1;Gb.a[1][1]=P-1; HA=Hb*Ha*Gb; HB=Hb*Hb*Ha*Gb*Gb; HC=Hb*Hb*Hb*Ga*Gb; W=(node){HA,HB}; E=(node){HB,HA}; scanf("%s",s+1);cnt=n+2; T.t[1][1]=2;T.fa[n+2]=n+1; for(int i=2;i<=n+1;i++){ if(s[i-1]=='W')T.a[i]=W; else T.a[i]=E; T.t[i][1]=i+1; T.fa[i]=i-1; } for(int i=n+2;i>=1;i--)T.PushUp(i); Query();int m=n; while(q--){ char op[10]; scanf("%s",op); if(op[0]=='A'){ scanf("%s",op);++cnt; if(op[0]=='W')T.a[cnt]=W; else T.a[cnt]=E; int p=T.Find(m+1); T.Splay(p,0);p=n+2; T.fa[cnt]=p;T.t[p][0]=cnt; T.PushUp(cnt);T.PushUp(p);++m; } else if(op[0]=='F'){ int l=read(),r=read(); l=T.Find(l);r=T.Find(r+2); T.Splay(l,0);T.Splay(r,l); T.PushU(T.t[r][0]); T.Splay(T.t[r][0],0); } else if(op[0]=='R'){ int l=read(),r=read(); l=T.Find(l);r=T.Find(r+2); T.Splay(l,0);T.Splay(r,l); T.PushR(T.t[r][0]); T.Splay(T.t[r][0],0); } Query(); } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16384409.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(83)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2021-06-17 P7115-[NOIP2020]移球游戏【构造】
点击右上角即可分享
微信分享提示