[NOI2021] 密码箱
\(\text{Problem}:\)[NOI2021] 密码箱
\(\text{Solution}:\)
首先,不难发现答案的分子和分母一定是互质的。具体的,考虑当前的答案为 \(\dfrac{x}{y}\),新加入一个 \(k\),答案变为 \(\dfrac{y+xk}{x}\),而显然 \(\gcd(x,y+xk)=\gcd(x,y)\)。
用向量维护当前的答案 \(\dfrac{x}{y}\),当从左边加入一个 \(k\) 时,易得 \(\left[\begin{array}{ccc}x&y \end{array}\right] \times \left[\begin{array}{ccc}k&1 \\ 1&0\end{array}\right]=\left[\begin{array}{ccc}y+xk&x\end{array}\right]\)。而 APPEND
是在序列末尾加操作,即操作顺序是从序列末尾向左,而每次操作均为右乘。
设每次操作在末尾加入一个矩阵 \(p\)。最后加入一个 W
操作时,则有 \(p\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]=\left[\begin{array}{ccc}k+1&1\\1&0\end{array}\right]\),易得 \(p=\left[\begin{array}{ccc}1&1\\0&1\end{array}\right]\)。最后加入一个 E
操作时,需要分类讨论:
- 若序列末尾为 \(1\),则 \(p\times \left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]=\left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k+1&1\\1&0\end{array}\right]\),得 \(p=\left[\begin{array}{ccc}2&-1\\1&0\end{array}\right]\)。
- 若序列末尾不为 \(1\),则 \(p\times \left[\begin{array}{ccc}x&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]=\left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}x-1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]\),得 \(p=\left[\begin{array}{ccc}2&-1\\1&0\end{array}\right]\)。
以上,发现对于 E
操作的两种情况对应的矩阵是相同的,故无需分类讨论。
考虑有区间反转和区间翻转的操作,容易联想到用平衡树去维护操作序列。为了方便求出答案,需要对每个结点 \(x\) 维护:\(x\) 是否存在反转操作;\(x\) 是否存在翻转操作;\(x\) 对应序列的前缀与后缀及其反转序列的前缀与后缀答案。总时间复杂度 \(O(n\log n)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=300010, Mod=998244353;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
} char inp[23];
int n,m;
char s[N];
int root,cnt,ch[N][2],rnd[N],siz[N],kd[N],rev[N],flip[N];
struct Mat { int a[2][2]; inline Mat () { memset(a,0,sizeof(a)); } };
inline Mat operator * (Mat x,Mat y)
{
Mat res;
for(ri int i=0;i<2;i++)
for(ri int k=0;k<2;k++)
for(ri int j=0;j<2;j++)
res.a[i][j]=(res.a[i][j]+1ll*x.a[i][k]*y.a[k][j]%Mod)%Mod;
return res;
}
Mat sum[N][4],val[N][2],bw,be,bas;
inline int New_Node(int tp)
{
rnd[++cnt]=rand();
siz[cnt]=1;
if(tp) val[cnt][0]=bw, val[cnt][1]=be;
else val[cnt][0]=be, val[cnt][1]=bw;
sum[cnt][0]=sum[cnt][1]=val[cnt][0], sum[cnt][2]=sum[cnt][3]=val[cnt][1];
return cnt;
}
inline void Push_Up(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
sum[x][0]=sum[ch[x][0]][0]*val[x][0]*sum[ch[x][1]][0];
sum[x][1]=sum[ch[x][1]][1]*val[x][0]*sum[ch[x][0]][1];
sum[x][2]=sum[ch[x][0]][2]*val[x][1]*sum[ch[x][1]][2];
sum[x][3]=sum[ch[x][1]][3]*val[x][1]*sum[ch[x][0]][3];
}
inline void Push_Down(int x)
{
if(rev[x])
{
swap(sum[ch[x][0]][0],sum[ch[x][0]][1]);
swap(sum[ch[x][0]][2],sum[ch[x][0]][3]);
swap(sum[ch[x][1]][0],sum[ch[x][1]][1]);
swap(sum[ch[x][1]][2],sum[ch[x][1]][3]);
swap(ch[ch[x][0]][0],ch[ch[x][0]][1]);
swap(ch[ch[x][1]][0],ch[ch[x][1]][1]);
if(ch[x][0]) rev[ch[x][0]]^=1;
if(ch[x][1]) rev[ch[x][1]]^=1;
rev[x]=0;
}
if(flip[x])
{
swap(sum[ch[x][0]][0],sum[ch[x][0]][2]);
swap(sum[ch[x][0]][1],sum[ch[x][0]][3]);
swap(sum[ch[x][1]][0],sum[ch[x][1]][2]);
swap(sum[ch[x][1]][1],sum[ch[x][1]][3]);
swap(val[ch[x][0]][0],val[ch[x][0]][1]);
swap(val[ch[x][1]][0],val[ch[x][1]][1]);
if(ch[x][0]) flip[ch[x][0]]^=1;
if(ch[x][1]) flip[ch[x][1]]^=1;
flip[x]=0;
}
}
void Split(int root,int k,int &x,int &y)
{
if(!root) { x=y=0; return; }
Push_Down(root);
if(siz[ch[root][0]]<k) x=root, Split(ch[root][1],k-siz[ch[root][0]]-1,ch[root][1],y);
else y=root, Split(ch[root][0],k,x,ch[root][0]);
Push_Up(root);
}
int Merge(int x,int y)
{
if(!x||!y) return x|y;
if(rnd[x]<rnd[y])
{
Push_Down(x);
ch[x][1]=Merge(ch[x][1],y);
Push_Up(x);
return x;
}
else
{
Push_Down(y);
ch[y][0]=Merge(x,ch[y][0]);
Push_Up(y);
return y;
}
}
inline void Insert(int tp)
{
root=Merge(root,New_Node(tp));
}
inline void Flip(int l,int r)
{
int x,y,z;
Split(root,l-1,x,y);
Split(y,r-l+1,y,z);
flip[y]^=1;
swap(val[y][0],val[y][1]);
swap(sum[y][0],sum[y][2]);
swap(sum[y][1],sum[y][3]);
root=Merge(x,Merge(y,z));
}
inline void Reverse(int l,int r)
{
int x,y,z;
Split(root,l-1,x,y);
Split(y,r-l+1,y,z);
rev[y]^=1;
swap(sum[y][0],sum[y][1]);
swap(sum[y][2],sum[y][3]);
swap(ch[y][0],ch[y][1]);
root=Merge(x,Merge(y,z));
}
signed main()
{
srand(time(NULL));
n=read(), m=read();
scanf("%s",s+1);
bw.a[0][0]=1, bw.a[0][1]=1, bw.a[1][1]=1;
be.a[0][0]=2, be.a[0][1]=Mod-1, be.a[1][0]=1;
for(ri int i=0;i<4;i++) sum[0][i].a[0][0]=sum[0][i].a[1][1]=1;
for(ri int i=1;i<=n;i++)
{
Insert(s[i]=='W');
}
Mat ans=sum[root][1]*bw;
printf("%d %d\n",ans.a[0][0],ans.a[0][1]);
for(ri int i=1;i<=m;i++)
{
scanf("%s",inp);
if(inp[0]=='A')
{
scanf("%s",inp);
Insert(inp[0]=='W');
}
else if(inp[0]=='F')
{
int l,r;
l=read(), r=read();
Flip(l,r);
}
else
{
int l,r;
l=read(), r=read();
Reverse(l,r);
}
Mat ans=sum[root][1]*bw;
printf("%d %d\n",ans.a[0][0],ans.a[0][1]);
}
return 0;
}