P7739 [NOI2021] 密码箱

题意:

Yelekastee 是 U 国著名的考古学家。在最近的一次考古行动中,他发掘出了一个远古时期的密码箱。经过周密而严谨的考证,Yelekastee 得知密码箱的密码和某一个数列 \(\{ a_n \}\) 相关。数列 \(\{ a_n \}\) 可以用如下方式构造出来:

  1. 初始时数列长度为 \(2\) 且有 \(a_0 = 0, a_1 = 1\)
  2. 对数列依次进行若干次操作,其中每次操作是以下两种类型之一:
  • W 类型:给数列的最后一项\(1\)
  • E 类型:若数列的最后一项\(1\),则给倒数第二项加 \(1\);否则先给数列的最后一项\(1\),接着在数列尾再加两项,两项的值都是 \(1\)

受到技术限制,密码箱并没有办法完整检查整个数列,因此密码箱的密码设定为数列 \(\{ a_n \}\) 经过函数 \(f\) 作用后的值,其中 \(f\) 的定义如下:

\[f(a_0, \ldots , a_{k - 1}, a_k) = \begin{cases} a_0, & k = 0 \\ f \! \left( a_0, a_1, \ldots , a_{k - 2}, a_{k - 1} + \frac{1}{a_k} \right) \! , & k \ge 1 \end{cases} \]

Yelekastee 并不擅长运算,因此他找到了你,希望你能根据他提供的操作序列计算出密码箱的密码。不幸的是,他的记性并不是很好,因此他会随时对提供的操作序列做出一些修改,这些修改包括以下三种:

  • APPEND c,在现有操作序列后追加一次 c 类型操作,其中 c 为字符 WE
  • FLIP l r,反转现有操作序列中第 \(l\) 个至第 \(r\) 个(下标从 \(1\) 开始,修改包含端点 \(l\)\(r\),下同)操作,即所有 W 变为 E,所有 E 变为 W
  • REVERSE l r,翻转现有操作序列中第 \(l\) 个至第 \(r\) 个操作,也就是将这个区间中的操作逆序。

分析:

发现 \(f\) 的倒数形如:

\[\frac{1}{a_{0}+\frac{1}{a_{1}+\frac{1}{a_{2}+\dots}}} \]

考虑从右往左加入 \(a_{i}\),将 \(\frac{a}{b} \rightarrow \frac{A}{B}\)

\[\frac{A}{B}=\frac{1}{a_{i}+\frac{a}{b}} \]

可以发现 \(A=b,\ B=a_{i} \times b+a\)

似乎可以使用矩阵乘法。

\[\begin{bmatrix} A \\ B \end{bmatrix} = \begin{bmatrix} 0 & 1 \\ 1 & a_{i} \end{bmatrix} \times \begin{bmatrix} a \\ b \end{bmatrix} \]

最后答案即为:

\[\begin{bmatrix} 0 & 1 \\ 1 & a_{0} \end{bmatrix} \times \begin{bmatrix} 0 & 1 \\ 1 & a_{1} \end{bmatrix} \times \begin{bmatrix} 0 & 1 \\ 1 & a_{2} \end{bmatrix} \times \dots \times \begin{bmatrix} 0 & 1 \\ 1 & a_{3} \end{bmatrix} \times \begin{bmatrix} 0 \\ 1 \end{bmatrix} \]

不难构造出,\(W\) 操作就是在后面乘上 \(\begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\)\(E\) 操作就是在后面乘上 \(\begin{bmatrix} 0 & -1 \\ 1 & 2 \end{bmatrix}\)

由于有翻转操作和取反操作,因此可以使用 FHQ-Treap 来维护。

具体地,平衡树每个节点维护以下信息:

  • 该节点的类型(是 \(W\) 还是 \(E\))。

  • 子树内的矩阵连乘积。

  • 子树内的翻转矩阵连乘积。

  • 子树内的取反矩阵连乘积。

  • 子树内的翻转取反矩阵连乘积。

  • 子树内的翻转标记。

  • 子树内的取反标记。

标记的合并只需要异或 \(1\) 即可。时间复杂度 \(O(n \log n)\)。常数巨大,轻度卡常。

代码:

#include<bits/stdc++.h>
#define N 200005
#define mod 998244353
#define ll long long
using namespace std;

struct node {
	int n, m;
	int a[5][5];
	friend node operator * (node x, node y) {
		node z;
		if(x.n == 2 && x.m == 2 && y.n == 2 && y.m == 2) {
			z.n = z.m = 2;
			z.a[1][1] = 1ll * ((long long)1ll * x.a[1][1] * y.a[1][1] % mod + (long long)1ll * x.a[1][2] * y.a[2][1] % mod + mod) % mod;
			z.a[1][2] = 1ll * ((long long)1ll * x.a[1][1] * y.a[1][2] % mod + (long long)1ll * x.a[1][2] * y.a[2][2] % mod + mod) % mod;
			z.a[2][1] = 1ll * ((long long)1ll * x.a[2][1] * y.a[1][1] % mod + (long long)1ll * x.a[2][2] * y.a[2][1] % mod + mod) % mod;
			z.a[2][2] = 1ll * ((long long)1ll * x.a[2][1] * y.a[1][2] % mod + (long long)1ll * x.a[2][2] * y.a[2][2] % mod + mod) % mod;
			return z;
		}
		else {
			z.n = 2; z.m = 1;
			z.a[1][1] = 1ll * ((long long)1ll * x.a[1][1] * y.a[1][1] % mod + (long long)1ll * x.a[1][2] * y.a[2][1] % mod + mod) % mod;
			z.a[2][1] = 1ll * ((long long)1ll * x.a[2][1] * y.a[1][1] % mod + (long long)1ll * x.a[2][2] * y.a[2][1] % mod + mod) % mod;
			return z;
		}
	}
};

void Out(node X) {
	cout << endl;
	for(int i = 1; i <= X.n; i++) {
		for(int j = 1; j <= X.m; j++) cout << X.a[i][j] << " ";
		cout << endl;
	}
	cout << endl;
}

node Empty() {
	node now;
	now.n = now.m = 2;
	now.a[1][1] = now.a[2][2] = 1;
	now.a[1][2] = now.a[2][1] = 0;
	return now;
}
int n, Q, root, cnt;
int ls[N], rs[N], siz[N], pri[N];
node My[N];
char r[N];
node val[N], val_f[N], val_r[N], val_fr[N];
bool tagf[N], tagr[N]; //f:是否取反 r:是否翻转 

node W, E, P, X;

int newnode(char opt) {
	cnt++;
	siz[cnt] = 1;
	pri[cnt] = rand() * rand(); 
	
	if(opt == 'E') {
		val[cnt] = val_r[cnt] = E;
		val_f[cnt] = val_fr[cnt] = W;
	}
	else {
		val[cnt] = val_r[cnt] = W;
		val_f[cnt] = val_fr[cnt] = E;
	}
	
	My[cnt] = val[cnt];
	r[cnt] = opt;
	return cnt;
}


void pushup(int u) {
	siz[u] = siz[ls[u]] + siz[rs[u]] + 1;
	val[u] = val[ls[u]] * My[u] * val[rs[u]];
	node now;
	if(r[u] == 'E') now = W;
	else now = E;
	val_f[u] = val_f[ls[u]] * now * val_f[rs[u]];
	val_r[u] = val_r[rs[u]] * My[u] * val_r[ls[u]];
	val_fr[u] = val_fr[rs[u]] * now * val_fr[ls[u]];
}

void maketag(int u, bool F, bool R) { //Flip, Revserse, 
	node A = val[u], B = val_r[u], C = val_f[u], D = val_fr[u];
	if(!F && R) { //只翻转
		swap(ls[u], rs[u]);
		val[u] = B;
		val_r[u] = A;
		val_f[u] = D;
		val_fr[u] = C;
		tagr[u] ^= 1;
	}
	else if(F && !R) { //只取反 
		val[u] = C;
		val_r[u] = D;
		val_f[u] = A;
		val_fr[u] = B;
		tagf[u] ^= 1;
		if(r[u] == 'E') {
			r[u] = 'W';
			My[u] = W;
		}
		else {
			r[u] = 'E';
			My[u] = E;
		}
	}
	else if(F && R) { //取反+翻转
		swap(ls[u], rs[u]);
		val[u] = D;
		val_r[u] = C;
		val_f[u] = B;
		val_fr[u] = A; 
		tagf[u] ^= 1;
		tagr[u] ^= 1;
		if(r[u] == 'E') {
			r[u] = 'W';
			My[u] = W;
		}
		else {
			r[u] = 'E';
			My[u] = E;
		}
	}
}

void pushdown(int u) {
	maketag(ls[u], tagf[u], tagr[u]);
	maketag(rs[u], tagf[u], tagr[u]);
	tagf[u] = tagr[u] = 0;
}

int merge(int x, int y) {
	if(x == 0 || y == 0) return x + y;
	pushdown(x); pushdown(y);
	if(pri[x] < pri[y]) {
		rs[x] = merge(rs[x], y);
		pushup(x);
		return x;
	} 
	else {
		ls[y] = merge(x, ls[y]);
		pushup(y);
		return y;
	}
}

void split(int u, int &x, int &y, int k) {
	if(u == 0) {
		x = y = 0;
		return;
	}
	pushdown(u);
	if(k <= siz[ls[u]]) {
		y = u;
		split(ls[u], x, ls[u], k);
	}
	else {
		x = u;
		split(rs[u], rs[u], y, k - siz[ls[u]] - 1);
	}
	pushup(u);
}

void Insert(char opt) {
	root = merge(root, newnode(opt));
}

string S, opt;

void Flip(int l, int r) {
	int x, y, z;
	split(root, x, y, l - 1);
	split(y, y, z, r - l + 1);
	maketag(y, 1, 0);
	root = merge(merge(x, y), z);
}

void Reverse(int l, int r) {
	int x, y, z;
	split(root, x, y, l - 1);
	split(y, y, z, r - l + 1);
	maketag(y, 0, 1);
	root = merge(merge(x, y), z);
}

void Print() {
	node Ans = P * val[root] * X;
	cout << Ans.a[2][1] << " " << Ans.a[1][1] << endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	val[0] = val_f[0] = val_r[0] = val_fr[0] = Empty();
	W.n = W.m = E.n = E.m = P.n = P.m = 2;
	W.a[1][1] = 1; W.a[1][2] = 1; W.a[2][1] = 0; W.a[2][2] = 1;
	E.a[1][1] = 0; E.a[1][2] = -1; E.a[2][1] = 1; E.a[2][2] = 2;
	P.a[1][1] = 1; P.a[1][2] = 1; P.a[2][1] = 0; P.a[2][2] = 1;
	X.n = 2; X.m = 1;
	X.a[1][1] = 0; X.a[2][1] = 1;
	cin >> n >> Q >> S;
	for(int i = 0; i < n; i++) Insert(S[i]);
	Print();
	while(Q--) {
		cin >> opt;
		if(opt[0] == 'A') { //append
			char c;
			cin >> c;
			Insert(c);
		}
		else if(opt[0] == 'F') {
			int l, r;
			cin >> l >> r;
			Flip(l, r); 
		}
		else {
			int l, r;
			cin >> l >> r;
			Reverse(l, r);
		}
		Print();
	}
	return 0;
}
posted @ 2024-04-18 22:02  小超手123  阅读(22)  评论(0编辑  收藏  举报