密码箱
题解
**平衡树,贞难调二世
由于题目要求答案必须是分数的最简,而在取模的意义下求
g
c
d
gcd
gcd又不大现实,我们先可以猜测我们直接求出来的答案一定是最简分数。
很容易发现,我们求答案的式子都是
a
+
y
x
a+\frac{y}{x}
a+xy的形式。在
(
x
,
y
)
=
1
(x,y)=1
(x,y)=1的情况下,很容易发现
(
a
x
+
y
,
x
)
=
1
(ax+y,x)=1
(ax+y,x)=1,所以我们求出的答案一定是最简的分数形式。
Point 1-4: 20pts
很容易想到的
O
(
n
q
)
O\left(nq\right)
O(nq)暴力修改查询,这就不详细说明了。
Point 5-7: 35pts
由于不会出现两个相邻的字符,所以我们的序列一定是
E
W
E
W
E
W
.
.
.
EWEWEW...
EWEWEW...或
W
E
W
E
W
E
.
.
.
WEWEWE...
WEWEWE...的形式。
经过暴力检验,我们可以发现它的答案一定是斐波拉契数。
于是,我们只需要维护第一个操作的种类。
对于
A
p
p
e
n
d
Append
Append操作,它只会影响操作序列的长度。
对于
F
l
i
p
Flip
Flip操作,只在针对整个序列反转时才有意义,会让我们第一个字符反转。
对于
R
e
v
e
r
s
e
Reverse
Reverse操作,它也只在针对整个序列的翻转时有意义,若序列长度为偶数会让第一个字符反转。
时间复杂度
O
(
n
+
q
)
O\left(n+q\right)
O(n+q)。
Point 8-10:50pts
我们可以考虑序列中的一个数会对答案造成怎样的影响,
f
(
a
,
x
y
)
=
a
x
+
y
x
f(a,\frac{x}{y})=\frac{ax+y}{x}
f(a,yx)=xax+y。
如果我们将分数看作一个数对显然是
(
x
,
y
)
→
(
a
x
+
y
,
x
)
(x,y)\rightarrow(ax+y,x)
(x,y)→(ax+y,x)。
这显然可以被写成矩阵相乘的形式,
(
x
,
y
)
(
a
,
1
1
,
0
)
=
(
a
x
+
y
,
x
)
(x,y)\left(\begin{array}{cc}a,1\\1,0\end{array}\right)=(ax+y,x)
(x,y)(a,11,0)=(ax+y,x)。
而在只有
A
p
p
e
n
d
Append
Append操作的情况下我们的
a
a
a序列是可以
O
(
1
)
O\left(1\right)
O(1)维护的,它只会影响后面的几个
a
a
a值,同理我们也可以
O
(
1
)
O\left(1\right)
O(1)维护后后面几个点的矩阵,乘起来得到答案。
时间复杂度
O
(
w
3
(
n
+
q
)
)
O\left(w^3(n+q)\right)
O(w3(n+q))。
Point 11-20:100pts
由上面的做法我们应该很快可以知道这道题可以利用矩阵解决,但很明显我们不能直接维护
a
a
a序列。
但是我们是知道
a
a
a序列的样子的,考虑每个操作会对其产生怎样的影响。
起初的
a
a
a序列
(
0
,
1
)
(0,1)
(0,1),而
W
W
W操作会让
a
n
a_{n}
an加一,也就是让
(
a
,
1
1
,
0
)
\left(\begin{array}{cc}a,1\\1,0\end{array}\right)
(a,11,0)变成
(
a
+
1
,
1
1
,
0
)
\left(\begin{array}{cc}a+1,1\\1,0\end{array}\right)
(a+1,11,0)。
很明显,这可以通过右乘矩阵
(
1
,
0
1
,
1
)
\left(\begin{array}{cc}1,0\\1,1\end{array}\right)
(1,01,1)来解决。
而
B
B
B操作的两种情况
(
a
+
1
,
b
)
(a+1,b)
(a+1,b),
(
a
,
b
−
1
,
1
,
1
)
(a,b-1,1,1)
(a,b−1,1,1)实际上得到的答案在
b
=
1
b=1
b=1的时候都是一样的,而我们也只有在
b
=
1
b=1
b=1的情况下会使
a
+
+
a++
a++,还不如都将其看作
(
a
,
b
)
⇒
(
a
,
b
−
1
,
1
,
1
)
(a,b)\Rightarrow(a,b-1,1,1)
(a,b)⇒(a,b−1,1,1)的操作。
而该操作我们可以看成乘
3
3
3个矩阵
(
1
,
0
−
1
,
1
)
(
1
,
1
1
,
0
)
(
1
,
1
1
,
0
)
=
(
2
,
1
−
1
,
0
)
\left(\begin{array}{cc}1,0\\-1,1\end{array}\right)\left(\begin{array}{cc}1,1\\1,0\end{array}\right)\left(\begin{array}{cc}1,1\\1,0\end{array}\right)=\left(\begin{array}{cc}2,1\\-1,0\end{array}\right)
(1,0−1,1)(1,11,0)(1,11,0)=(2,1−1,0)。
于是我们所有的操作都相当于乘上一个矩阵。
而我们的
A
p
p
e
n
d
Append
Append操作相当与多乘一个矩阵,
F
l
i
p
Flip
Flip矩阵相当于将
W
W
W操作的矩阵与
B
B
B操作的矩阵调换,
R
e
v
e
r
s
e
Reverse
Reverse操作相当于将一个区间内矩阵乘的顺序反转。
这些从操作都可以通过平衡树来进行维护,但由于踏实区间操作,我们只能通过懒标记的下传来更新,不能直接更改,所以对于一个区间我们要维护
4
4
4个矩阵,不同种类以及不同顺序相乘得到的矩阵。
每次交换和反转可以直接通过交换这四个矩阵来实现快速更新,方便懒标记的下传。
最后乘上初始序列的
{
a
0
,
a
1
}
\{a_0,a_1\}
{a0,a1}的矩阵
(
1
,
0
1
,
1
)
\left(\begin{array}{cc}1,0\\1,1\end{array}\right)
(1,01,1)即可。
时间复杂度 O ( w 3 q l o g n ) O\left(w^3qlog\,n\right) O(w3qlogn)。
源码
调了好久
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int mo=998244353;
const int jzm=2333;
const int lim=10000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
struct matrix{
int c[3][3];matrix(){memset(c,0,sizeof(c));}
matrix operator * (const matrix &rhs)const{
matrix res;
for(int i=1;i<3;i++)
for(int k=1;k<3;k++)
for(int j=1;j<3;j++)
res.c[i][j]=add(res.c[i][j],1ll*c[i][k]*rhs.c[k][j]%mo,mo);
return res;
}
void print(){
for(int i=1;i<3;i++,puts(""))
for(int j=1;j<3;j++)printf("%d ",c[i][j]);
puts("");
}
}Ia,Ib,Id;
int n,q;char str[MAXN];
struct ming{
int ch[2],siz,rnd;matrix d[4],vala,valb;bool rev,lzy;
ming(){ch[0]=ch[1]=siz=rnd=rev=lzy=0;}
};
class FHQ_Treap{
private:
ming tr[MAXN];int tot,root;
int newnode(int typ){
int rt=++tot;tr[rt].siz=1;tr[rt].rnd=rand();
tr[rt].vala=tr[rt].d[0]=tr[rt].d[2]=(typ?Ia:Ib);
tr[rt].valb=tr[rt].d[1]=tr[rt].d[3]=(typ?Ib:Ia);
return rt;
}
void pushup(int rt){
tr[rt].siz=1+tr[tr[rt].ch[0]].siz+tr[tr[rt].ch[1]].siz;tr[rt].d[0]=tr[rt].d[2]=tr[rt].vala;tr[rt].d[1]=tr[rt].d[3]=tr[rt].valb;
if(tr[rt].ch[0])
tr[rt].d[0]=tr[tr[rt].ch[0]].d[0]*tr[rt].d[0],tr[rt].d[1]=tr[tr[rt].ch[0]].d[1]*tr[rt].d[1],
tr[rt].d[2]=tr[rt].d[2]*tr[tr[rt].ch[0]].d[2],tr[rt].d[3]=tr[rt].d[3]*tr[tr[rt].ch[0]].d[3];
if(tr[rt].ch[1])
tr[rt].d[0]=tr[rt].d[0]*tr[tr[rt].ch[1]].d[0],tr[rt].d[1]=tr[rt].d[1]*tr[tr[rt].ch[1]].d[1],
tr[rt].d[2]=tr[tr[rt].ch[1]].d[2]*tr[rt].d[2],tr[rt].d[3]=tr[tr[rt].ch[1]].d[3]*tr[rt].d[3];
}
void Flip(int rt){swap(tr[rt].vala,tr[rt].valb);swap(tr[rt].d[0],tr[rt].d[1]);swap(tr[rt].d[2],tr[rt].d[3]);tr[rt].lzy^=1;}
void Reverse(int rt){swap(tr[rt].ch[0],tr[rt].ch[1]);swap(tr[rt].d[0],tr[rt].d[2]);swap(tr[rt].d[1],tr[rt].d[3]);tr[rt].rev^=1;}
void pushdown(int rt){
if(tr[rt].lzy){if(tr[rt].ch[0])Flip(tr[rt].ch[0]);if(tr[rt].ch[1])Flip(tr[rt].ch[1]);tr[rt].lzy=0;}
if(tr[rt].rev){if(tr[rt].ch[0])Reverse(tr[rt].ch[0]);if(tr[rt].ch[1])Reverse(tr[rt].ch[1]);tr[rt].rev=0;}
}
void split(int now,int k,int &x,int &y){
if(!now){x=y=0;return ;}pushdown(now);
if(k>tr[tr[now].ch[0]].siz)
x=now,split(tr[now].ch[1],k-tr[tr[now].ch[0]].siz-1,tr[x].ch[1],y),pushup(x);
else y=now,split(tr[now].ch[0],k,x,tr[y].ch[0]),pushup(y);
}
int merge(int a,int b){
if(!a||!b)return a+b;
pushdown(a);pushdown(b);
if(tr[a].rnd<tr[b].rnd){
tr[a].ch[1]=merge(tr[a].ch[1],b);
pushup(a);return a;
}
tr[b].ch[0]=merge(a,tr[b].ch[0]);
pushup(b);return b;
}
void build(int &rt,int l,int r){
int mid=l+r>>1;rt=newnode(str[mid]=='W');
if(l<mid)build(tr[rt].ch[0],l,mid-1);
if(r>mid)build(tr[rt].ch[1],mid+1,r);
pushup(rt);
}
public:
void Build(int len){build(root,1,len);}
void Append(int typ){root=merge(root,newnode(typ));}
void MakeFlip(int l,int r){
int x1,y1,x2,y2;split(root,r,x1,y1);
split(x1,l-1,x2,y2);Flip(y2);
root=merge(merge(x2,y2),y1);
}
void MakeReverse(int l,int r){
int x1,y1,x2,y2;split(root,r,x1,y1);
split(x1,l-1,x2,y2);Reverse(y2);
root=merge(merge(x2,y2),y1);
}
pii Query(){
matrix res=Id*tr[root].d[0];
return mkpr(res.c[1][1],res.c[2][1]);
}
}T;
signed main(){
//freopen("code.in","r",stdin);
//freopen("code.out","w",stdout);
srand(114514);read(n);read(q);
Id.c[1][1]=1;Id.c[2][1]=1;Id.c[2][2]=1;
Ia.c[1][1]=1;Ia.c[2][2]=1;Ia.c[2][1]=1;
Ib.c[1][1]=2;Ib.c[2][1]=mo-1;Ib.c[1][2]=1;
scanf("\n%s",str+1);T.Build(n);
pii res=T.Query();printf("%d %d\n",res.fir,res.sec);
for(int i=1;i<=q;i++){
char opt[10]={},ch;int l,r;scanf("\n%s",opt+1);
if(opt[1]=='A')scanf(" %c",&ch),T.Append(ch=='W');
else if(opt[1]=='F')read(l),read(r),T.MakeFlip(l,r);
else read(l),read(r),T.MakeReverse(l,r);
pii res=T.Query();printf("%d %d\n",res.fir,res.sec);
}
return 0;
}