题目
题目背景
今天上午,我看到卷爷竟然在倒着跑步,姿势非常奇怪,简直仿佛是违背了物理规律。他往机房倒着跑去,一会儿消失在了我的视野里。
在机房楼下,我又见到卷爷,他这时却在正着走,完全没有刚才奇怪的神态,仿佛换了个人似的。我虽然疑惑,却又无权发声,去问全知全能的神——卷爷。我只好上去。
考试结束了。卷爷很自然地 A K AK AK 了——他的表情没有波澜,他的心跳没有加速。是习以为常了吧。不过,我的脑海中还盘旋着另一个猜想:卷爷考试时的不紧不慢、胸有成竹,似乎都表明,他知道自己会 A K AK AK 。
我刚想开口问他,他忽然回头,双手十指交叉,说:“不是我知道我会 A K AK AK,而是我知道我已经行将 A K AK AK 了。我们把这叫做, r e a l i t y \rm reality reality 。”
题目描述
传送门 to luogu
思路
如果我们不考虑连分数从前往后递推的方式,则只能考虑从后往前模拟。显然不必约分。设已经得到
p
q
p\over q
qp,则下一步得到
[
a
i
1
1
0
]
[
p
q
]
\begin{bmatrix} a_i & 1\\ 1 & 0 \end{bmatrix} \begin{bmatrix} p\\ q \end{bmatrix}
[ai110][pq]
经过卷爷的提醒,知道大概需平衡树维护。
n
a
i
v
e
\rm naive
naive 想法:每个区间维护最后两个数,和最开头的一段 E
和 W
长度。因为这些是只凭区间内元素无法确定的信息。
奇葩的是,不必讨论。W
等价于
[
a
i
+
λ
1
1
0
]
=
[
a
i
1
1
0
]
[
1
0
λ
1
]
\begin{bmatrix} a_i{+}\lambda & 1\\ 1 & 0 \end{bmatrix} =\begin{bmatrix} a_i & 1\\ 1 & 0 \end{bmatrix} \begin{bmatrix} 1 & 0\\ \lambda & 1 \end{bmatrix}
[ai+λ110]=[ai110][1λ01]
即乘上一个额外的矩阵。注意必须要构造右乘的矩阵。
对于 E
来说,如果末项为
1
1
1,发生的变化是
[
1
1
1
0
]
→
[
1
0
1
1
]
[
1
1
1
0
]
=
[
1
1
2
1
]
\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix} \to \begin{bmatrix} 1 & 0\\ 1 & 1 \end{bmatrix} \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix} =\begin{bmatrix} 1 & 1\\ 2 & 1 \end{bmatrix}
[1110]→[1101][1110]=[1211]
否则直接右乘
[
1
0
−
1
1
]
[
1
1
1
0
]
2
=
[
2
1
−
1
0
]
\begin{bmatrix} 1 & 0\\ -1 & 1 \end{bmatrix} \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^2 =\begin{bmatrix} 2 & 1\\ -1 & 0 \end{bmatrix}
[1−101][1110]2=[2−110]
其实乘了同一个矩阵。验算即知,下面这个矩阵也能起到上面的变化效果。
所以问题变成了裸的平衡树维护矩阵积,支持翻转和取反。时间复杂度 O ( n log n ) \mathcal O(n\log n) O(nlogn) 。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype> // isdigit
#include <random>
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a*f;
}
const int MOD = 998244353;
struct Matrix{
int a[2][2];
Matrix operator * (const Matrix &b) const {
Matrix c;
c.a[0][0] = int((llong(a[0][0])*b.a[0][0]
+llong(a[0][1])*b.a[1][0])%MOD);
c.a[0][1] = int((llong(a[0][0])*b.a[0][1]
+llong(a[0][1])*b.a[1][1])%MOD);
c.a[1][0] = int((llong(a[1][0])*b.a[0][0]
+llong(a[1][1])*b.a[1][0])%MOD);
c.a[1][1] = int((llong(a[1][0])*b.a[0][1]
+llong(a[1][1])*b.a[1][1])%MOD);
return c;
}
Matrix& operator *= (const Matrix &b){
return *this = (*this)*b;
}
};
Matrix trans(const char &me){
static Matrix adder = Matrix{1,0,1,1};
static Matrix exer = Matrix{2,1,MOD-1,0};
return me == 'W' ? adder : exer;
}
const int MAXN = 200005;
namespace treap{
struct Node{
Node* ch[2]; unsigned prio;
Matrix val[2][2]; // direction and reverse
bool revtag, fliptag; char me; int siz;
inline void pushUp(){
val[0][0] = val[1][0] = trans(me);
val[0][1] = val[1][1] = trans('W'^'E'^me);
rep(dir,0,1) rep(o,0,1){
if(ch[dir] != nullptr) // prefered child
val[dir][o] = ch[dir]->val[dir][o]*val[dir][o];
if(ch[dir^1] != nullptr) // after me
val[dir][o] *= ch[dir^1]->val[dir][o];
}
siz = 1;
if(ch[0] != nullptr) siz += ch[0]->siz;
if(ch[1] != nullptr) siz += ch[1]->siz;
}
inline void rev(){ // reverse color
revtag = !revtag, me = 'W'^'E'^me;
rep(d,0,1) std::swap(val[d][0],val[d][1]);
}
inline void flip(){
fliptag = !fliptag, std::swap(ch[0],ch[1]);
rep(o,0,1) std::swap(val[0][o],val[1][o]);
}
inline void pushDown(){
if(fliptag){
if(ch[0] != nullptr) ch[0]->flip();
if(ch[1] != nullptr) ch[1]->flip();
fliptag = false;
}
if(revtag){
if(ch[0] != nullptr) ch[0]->rev();
if(ch[1] != nullptr) ch[1]->rev();
revtag = false;
}
}
};
Node _node[MAXN], *_alloc = _node;
inline Node* newNode(const char &c){
static std::mt19937 rnd;
Node* o = _alloc ++;
o->prio = unsigned(rnd());
o->me = c, o->pushUp(); return o;
}
Node* merge(Node* a, Node* b){
if(a == nullptr) return b;
if(b == nullptr) return a;
if(a->prio > b->prio){
a->pushDown(), a->ch[1] = merge(a->ch[1],b);
a->pushUp(); return a;
}
b->pushDown(), b->ch[0] = merge(a,b->ch[0]);
b->pushUp(); return b;
}
void split(Node* o, int k, Node* &x, Node* &y){
if(!k){ x = nullptr; y = o; return; }
o->pushDown(); // not to be empty
if(o->ch[0] == nullptr) // since k >= 1
split(o->ch[1],k-1,o->ch[1],y), x = o, o->pushUp();
else if(o->ch[0]->siz+1 <= k) // to be left
split(o->ch[1],k-o->ch[0]->siz-1,o->ch[1],y), x = o, o->pushUp();
else split(o->ch[0],k,x,o->ch[0]), y = o, o->pushUp();
}
Node* rt = nullptr;
inline void append(const char &c){
rt = merge(rt,newNode(c));
}
void flip(int l, int r){
Node *lp, *mid, *rp; split(rt,r,lp,rp);
split(lp,l-1,lp,mid); mid->flip();
rt = merge(merge(lp,mid),rp);
}
void rev(int l, int r){
Node *lp, *mid, *rp; split(rt,r,lp,rp);
split(lp,l-1,lp,mid); mid->rev();
rt = merge(merge(lp,mid),rp);
}
void query(){
static Matrix _init = Matrix{1,1,1,0};
Matrix ans = rt->val[0][0]; ans = _init*ans;
printf("%d %d\n",ans.a[1][0],ans.a[0][0]);
}
}
char cmd[10];
int main(){
int n = readint(), q = readint();
rep(i,1,n) treap::append(char(getchar()));
treap::query();
for(int x; q; --q){
scanf("%s",cmd);
if(*cmd == 'A') scanf("%s",cmd), treap::append(*cmd);
else if(*cmd == 'R') x = readint(), treap::flip(x,readint());
else x = readint(), treap::rev(x,readint());
treap::query();
}
return 0;
}
后记
O U Y E \sf OUYE OUYE 一年前考场上怒切此题, O n e I n D a r k \sf OneInDark OneInDark 今天还不会做去贺题解。人终究不能和神比。