[NOI2021]密码箱

280 篇文章 1 订阅
137 篇文章 1 订阅

题目

题目背景
今天上午,我看到卷爷竟然在倒着跑步,姿势非常奇怪,简直仿佛是违背了物理规律。他往机房倒着跑去,一会儿消失在了我的视野里。

在机房楼下,我又见到卷爷,他这时却在正着走,完全没有刚才奇怪的神态,仿佛换了个人似的。我虽然疑惑,却又无权发声,去问全知全能的神——卷爷。我只好上去。

考试结束了。卷爷很自然地 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 想法:每个区间维护最后两个数,和最开头的一段 EW 长度。因为这些是只凭区间内元素无法确定的信息。

奇葩的是,不必讨论。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} [1101][1110]2=[2110]

其实乘了同一个矩阵。验算即知,下面这个矩阵也能起到上面的变化效果。

所以问题变成了裸的平衡树维护矩阵积,支持翻转和取反。时间复杂度 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 今天还不会做去贺题解。人终究不能和神比。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值