[NOI2021] 密码箱
前言
我大概是全场(同步赛)唯一一个靠找规律做出这题的吧。
挺刺激的,最后三分钟肉眼找到Treap的最后一个错误。
我虽然考前从这篇博客了解过连分数,不过当时压根没想过NOI会考这个,连递推式都搞忘了。
思路
观察函数
f
f
f 的求法,其实就是在算连分数。设第
i
i
i 渐进分数为
p
i
q
i
\frac{p_i}{q_i}
qipi,数列第
i
i
i 项为
a
i
a_i
ai,那么有如下递推关系:
p
i
=
a
i
p
i
−
1
+
p
i
−
2
q
i
=
a
i
q
i
−
1
+
q
i
−
2
p_i=a_ip_{i-1}+p_{i-2}\\q_i=a_iq_{i-1}+q_{i-2}
pi=aipi−1+pi−2qi=aiqi−1+qi−2
我们要求的就是
p
n
−
1
q
n
−
1
\frac{p_{n-1}}{q_{n-1}}
qn−1pn−1 。由于连分数保证了是最简分数,所以
由于分子分母的的递推式完全一样,且互不相干,所以我们只需用不同的初值、同样的方法把分子分母各求一遍。
(下面只讨论分子)容易发现下一个分子只与前两个分子有关,于是设当前最后两个分子为
{
a
,
b
}
\{a,b\}
{a,b},我们分W
和E
讨论它的变化情况:
- 一次
W
操作,序列末尾从 { a a , a b } \{a_a,a_b\} {aa,ab} 变为 { a a , a b + 1 } \{a_a,a_b+1\} {aa,ab+1},根据递推式,分子变为 { a , a + b } \{a,a+b\} {a,a+b}。 - 一次
E
操作,又要分两种情况:- 最后一项不为1,序列尾变为 { a a , a b − 1 , 1 , 1 } \{a_a,a_b-1,1,1\} {aa,ab−1,1,1} ,所以最后两个分子发生如下变化: { a , b } → { a , b − a } → { b − a , b } → { b , 2 b − a } \{a,b\}\rightarrow \{a,b-a\}\rightarrow \{b-a,b\}\rightarrow \{b,2b-a\} {a,b}→{a,b−a}→{b−a,b}→{b,2b−a}。
- 最后一项为1,那么假设倒数第3个分子为 c c c,那么序列倒数第二项加一会导致最后两个分子变为 { a + c , b + c } \{a+c,b+c\} {a+c,b+c}。由于最后一项为1,所以保证 b = a + c b=a+c b=a+c,于是 a + c = b , b + c = 2 b − a a+c=b,b+c=2b-a a+c=b,b+c=2b−a,最后两个分子还是变为了 { b , 2 b − a } \{b,2b-a\} {b,2b−a}。
于是我们发现,操作为W
或E
时分子或分母的变化方式是固定且一次的,所以可以用矩阵乘法代替普通的DP,这题的转移就有了可动态维护的性质。
我们可以用平衡树维护操作序列每个位置和一段区间的转移矩阵,并同时维护它的相反矩阵和翻转矩阵(共4个矩阵),然后就可以实现在操作序列末尾加入新操作、反转序列区间、翻转序列区间等。这和普通动态DP用线段树维护不一样,带区间翻转操作的动态DP只能用平衡树。
关于我在考试时发现的规律,其实不是我上面写的这个转移式。
我把操作后
f
f
f 函数的计算结果的变化全部打下表来
这让我想起了在做这道题时看到的某个卷毛香猪的博客里介绍的
S
t
e
r
n
−
B
r
o
c
o
t
\rm Stern-Brocot
Stern−Brocot 树,同样是分数,同样是二叉树,同样以
1
1
\frac{1}{1}
11 为根。两者长得竟然一模一样
!
S
t
e
r
n
−
B
r
o
c
o
t
\rm Stern-Brocot
Stern−Brocot 树上的分数都有一个非常优美且简单的规律:分子或分母等于它两侧虚线上的分子或分母相加。
于是转移式子直接出来了!而且最简分数无需约分!直接套路动态DP啊!
于是我激动不已,以至于我打了一半线段树才突然想到用平衡树,代码成形后一堆错误,然后删删改改…
代码
连分数递推版
#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<ctime>
#include<map>
#define ll long long
#define MAXN 200005
#define uns unsigned
#define INF 1e17
#define MOD 998244353ll
#define lowbit(x) ((x)&(-(x)))
#define ull uns ll
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
inline ll RAND(){return 1ll*rand()*rand();}
int n,q,m;
char s[MAXN];
struct matrix{
int n,m;
ll c[2][2];
matrix(){memset(c,0,sizeof(c)),n=m=0;}
matrix operator*(const matrix&b){
matrix r;r.n=n,r.m=b.m;
for(int i=0;i<n;i++)
for(int k=0;k<m;k++)
for(int j=0;j<r.m;j++)
r.c[i][j]=(r.c[i][j]+c[i][k]*b.c[k][j])%MOD;
return r;
}
}E,A,B,aa,bb,f,g;
struct node{
int x,y;node(){}
node(int X,int Y){x=X,y=Y;}
};
struct itn{
int ls,rs,siz,val;
matrix a,c,sa,sb,sc,sd;
bool p,q;
itn(){a=c=sa=sb=sc=sd=E,p=q=0,ls=rs=0,siz=0;}
}t[MAXN];
int root,IN;
inline void PD(int x){
if(t[x].p){
if(t[x].ls)
swap(t[t[x].ls].a,t[t[x].ls].c),
swap(t[t[x].ls].sa,t[t[x].ls].sc),swap(t[t[x].ls].sb,t[t[x].ls].sd),
t[t[x].ls].p^=1;
if(t[x].rs)
swap(t[t[x].rs].a,t[t[x].rs].c),
swap(t[t[x].rs].sa,t[t[x].rs].sc),swap(t[t[x].rs].sb,t[t[x].rs].sd),
t[t[x].rs].p^=1;
t[x].p=0;
}
if(t[x].q){
if(t[x].ls)swap(t[t[x].ls].sa,t[t[x].ls].sb),swap(t[t[x].ls].sc,t[t[x].ls].sd),
t[t[x].ls].q^=1,swap(t[t[x].ls].ls,t[t[x].ls].rs);
if(t[x].rs)swap(t[t[x].rs].sa,t[t[x].rs].sb),swap(t[t[x].rs].sc,t[t[x].rs].sd),
t[t[x].rs].q^=1,swap(t[t[x].rs].ls,t[t[x].rs].rs);
t[x].q=0;
}
}
inline void update(int x){
t[x].sa=t[t[x].ls].sa*t[x].a*t[t[x].rs].sa;
t[x].sb=t[t[x].rs].sb*t[x].a*t[t[x].ls].sb;
t[x].sc=t[t[x].ls].sc*t[x].c*t[t[x].rs].sc;
t[x].sd=t[t[x].rs].sd*t[x].c*t[t[x].ls].sd;
t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
}
inline node split(int x,int k){
if(!x||!k)return node(0,x);
PD(x);node res;int si=t[t[x].ls].siz;
if(si>=k)
res=split(t[x].ls,k),t[x].ls=res.y,update(x),res.y=x;
else
res=split(t[x].rs,k-si-1),t[x].rs=res.x,update(x),res.x=x;
return res;
}
inline int mergg(int x,int y){
if(!x||!y)return x|y;
PD(x),PD(y);int res;
if(t[x].val<=t[y].val)
t[x].rs=mergg(t[x].rs,y),update(x),res=x;
else t[y].ls=mergg(x,t[y].ls),update(y),res=y;
return res;
}
inline void CP(int x){
t[x].sa=t[x].a,t[x].sb=t[x].a,t[x].sc=t[x].c,t[x].sd=t[x].c;
}
char in[15],cc;
ll ansa,ansb;
signed main()
{
// freopen("code.in","r",stdin);
// freopen("code.out","w",stdout);
srand(time(0));
A.n=A.m=B.n=B.m=2,E.n=E.m=2;
E.c[0][0]=E.c[1][1]=1;
A.c[0][0]=A.c[0][1]=A.c[1][1]=1;
B.c[0][1]=MOD-1,B.c[1][0]=1,B.c[1][1]=2;
aa.n=bb.n=1,aa.m=bb.m=2;
aa.c[0][1]=bb.c[0][0]=bb.c[0][1]=1;
t[0]=itn();
n=read(),q=read(),m=n+q;
scanf("%s",s+1);
for(int i=n+1;i<=m;i++)s[i]='W';
for(int i=1;i<=n;i++){
IN++,t[IN]=itn(),t[IN].siz=1;
if(s[i]=='W')t[IN].a=A,t[IN].c=B;
else t[IN].a=B,t[IN].c=A;
CP(IN),t[IN].val=RAND()%MAXN,root=mergg(root,IN);
}
f=t[root].sa;
g=aa*f,ansa=g.c[0][1];
g=bb*f,ansb=g.c[0][1];
printf("%lld %lld\n",ansa,ansb);
while(q--){
scanf("%s",in);
if(in[0]=='A'){
cc=getchar();
while(cc!='W'&&cc!='E')cc=getchar();
n++,s[n]=cc,IN++,t[IN]=itn(),t[IN].siz=1;
if(s[n]=='W')t[IN].a=A,t[IN].c=B;
else t[IN].a=B,t[IN].c=A;
CP(IN),t[IN].val=RAND()%MAXN,root=mergg(root,IN);
}
else if(in[0]=='F'){
int l=read(),r=read();
node x=split(root,l-1),y=split(x.y,r-l+1);
t[y.x].p^=1;
swap(t[y.x].a,t[y.x].c),
swap(t[y.x].sa,t[y.x].sc),swap(t[y.x].sb,t[y.x].sd);
root=mergg(x.x,mergg(y.x,y.y));
}
else{
int l=read(),r=read();
node x=split(root,l-1),y=split(x.y,r-l+1);
t[y.x].q^=1;
swap(t[y.x].sa,t[y.x].sb),swap(t[y.x].sc,t[y.x].sd);
swap(t[y.x].ls,t[y.x].rs);
root=mergg(x.x,mergg(y.x,y.y));
}
f=t[root].sa;
g=aa*f,ansa=g.c[0][1];
g=bb*f,ansb=g.c[0][1];
printf("%lld %lld\n",ansa,ansb);
}
return 0;
}
S t e r n − B r o c o t \rm Stern-Brocot Stern−Brocot 树版(两者只有初始和转移矩阵及提取答案的区别)
#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<ctime>
#include<map>
#define ll long long
#define MAXN 200005
#define uns unsigned
#define INF 1e17
#define MOD 998244353ll
#define lowbit(x) ((x)&(-(x)))
#define ull uns ll
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
inline ll RAND(){return 1ll*rand()*rand();}
int n,q,m;
char s[MAXN];
struct matrix{
int n,m;
ll c[2][2];
matrix(){memset(c,0,sizeof(c)),n=m=0;}
matrix operator*(const matrix&b){
matrix r;r.n=n,r.m=b.m;
for(int i=0;i<n;i++)
for(int k=0;k<m;k++)
for(int j=0;j<r.m;j++)
r.c[i][j]=(r.c[i][j]+c[i][k]*b.c[k][j])%MOD;
return r;
}
}E,A,B,aa,bb,f,g;
struct node{
int x,y;node(){}
node(int X,int Y){x=X,y=Y;}
};
struct itn{
int ls,rs,siz,val;
matrix a,c,sa,sb,sc,sd;
bool p,q;
itn(){a=c=sa=sb=sc=sd=E,p=q=0,ls=rs=0,siz=0;}
}t[MAXN];
int root,IN;
inline void PD(int x){
if(t[x].p){
if(t[x].ls)
swap(t[t[x].ls].a,t[t[x].ls].c),
swap(t[t[x].ls].sa,t[t[x].ls].sc),swap(t[t[x].ls].sb,t[t[x].ls].sd),
t[t[x].ls].p^=1;
if(t[x].rs)
swap(t[t[x].rs].a,t[t[x].rs].c),
swap(t[t[x].rs].sa,t[t[x].rs].sc),swap(t[t[x].rs].sb,t[t[x].rs].sd),
t[t[x].rs].p^=1;
t[x].p=0;
}
if(t[x].q){
if(t[x].ls)swap(t[t[x].ls].sa,t[t[x].ls].sb),swap(t[t[x].ls].sc,t[t[x].ls].sd),
t[t[x].ls].q^=1,swap(t[t[x].ls].ls,t[t[x].ls].rs);
if(t[x].rs)swap(t[t[x].rs].sa,t[t[x].rs].sb),swap(t[t[x].rs].sc,t[t[x].rs].sd),
t[t[x].rs].q^=1,swap(t[t[x].rs].ls,t[t[x].rs].rs);
t[x].q=0;
}
}
inline void update(int x){
t[x].sa=t[t[x].ls].sa*t[x].a*t[t[x].rs].sa;
t[x].sb=t[t[x].rs].sb*t[x].a*t[t[x].ls].sb;
t[x].sc=t[t[x].ls].sc*t[x].c*t[t[x].rs].sc;
t[x].sd=t[t[x].rs].sd*t[x].c*t[t[x].ls].sd;
t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
}
inline node split(int x,int k){
if(!x||!k)return node(0,x);
PD(x);node res;int si=t[t[x].ls].siz;
if(si>=k)
res=split(t[x].ls,k),t[x].ls=res.y,update(x),res.y=x;
else
res=split(t[x].rs,k-si-1),t[x].rs=res.x,update(x),res.x=x;
return res;
}
inline int mergg(int x,int y){
if(!x||!y)return x|y;
PD(x),PD(y);int res;
if(t[x].val<=t[y].val)
t[x].rs=mergg(t[x].rs,y),update(x),res=x;
else t[y].ls=mergg(x,t[y].ls),update(y),res=y;
return res;
}
inline void CP(int x){
t[x].sa=t[x].a,t[x].sb=t[x].a,t[x].sc=t[x].c,t[x].sd=t[x].c;
}
char in[15],cc;
ll ansa,ansb;
signed main()
{
// freopen("code.in","r",stdin);
// freopen("code.out","w",stdout);
srand(time(0));
A.n=A.m=B.n=B.m=2,E.n=E.m=2;
E.c[0][0]=E.c[1][1]=1;
A.c[0][0]=A.c[0][1]=A.c[1][1]=1;
B.c[0][0]=B.c[1][0]=B.c[1][1]=1;
aa.n=bb.n=1,aa.m=bb.m=2;
aa.c[0][1]=bb.c[0][0]=1;
t[0]=itn();
n=read(),q=read(),m=n+q;
scanf("%s",s+1);
for(int i=n+1;i<=m;i++)s[i]='W';
for(int i=1;i<=n;i++){
IN++,t[IN]=itn(),t[IN].siz=1;
if(s[i]=='W')t[IN].a=A,t[IN].c=B;
else t[IN].a=B,t[IN].c=A;
CP(IN),t[IN].val=RAND()%MAXN,root=mergg(root,IN);
}
f=t[root].sa;
g=aa*f,ansa=(g.c[0][1]+g.c[0][0])%MOD;
g=bb*f,ansb=(g.c[0][1]+g.c[0][0])%MOD;
printf("%lld %lld\n",ansa,ansb);
while(q--){
scanf("%s",in);
if(in[0]=='A'){
cc=getchar();
while(cc!='W'&&cc!='E')cc=getchar();
n++,s[n]=cc,IN++,t[IN]=itn(),t[IN].siz=1;
if(s[n]=='W')t[IN].a=A,t[IN].c=B;
else t[IN].a=B,t[IN].c=A;
CP(IN),t[IN].val=RAND()%MAXN,root=mergg(root,IN);
}
else if(in[0]=='F'){
int l=read(),r=read();
node x=split(root,l-1),y=split(x.y,r-l+1);
t[y.x].p^=1;
swap(t[y.x].a,t[y.x].c),
swap(t[y.x].sa,t[y.x].sc),swap(t[y.x].sb,t[y.x].sd);
root=mergg(x.x,mergg(y.x,y.y));
}
else{
int l=read(),r=read();
node x=split(root,l-1),y=split(x.y,r-l+1);
t[y.x].q^=1;
swap(t[y.x].sa,t[y.x].sb),swap(t[y.x].sc,t[y.x].sd);
swap(t[y.x].ls,t[y.x].rs);
root=mergg(x.x,mergg(y.x,y.y));
}
f=t[root].sa;
g=aa*f,ansa=(g.c[0][1]+g.c[0][0])%MOD;
g=bb*f,ansb=(g.c[0][1]+g.c[0][0])%MOD;
printf("%lld %lld\n",ansa,ansb);
}
return 0;
}