首页 > 其他分享 >洛谷 P8077 [WC2022] 序列变换 题解

洛谷 P8077 [WC2022] 序列变换 题解

时间:2023-01-12 15:24:55浏览次数:60  
标签:洛谷 右链 rs int 题解 back WC2022 ls 操作

题目链接

WC2023 之前补一下 WC2022 的题,参考了官方题解。

首先,把括号序列转化为二叉树,\(\texttt{(A)B}\) 转为一个点的左子树是 \(A\),右子树是 \(B\)。相当于括号序列先转为森林,再通过左儿子右兄弟转化为二叉树。

前四种操作对应的树上操作:

8077_01

操作 1、4 类似于平衡树的旋转。这四种操作都不会改变二叉树的中序遍历。

操作 5、6 相当于添加、删除一个没有左儿子的点。

考虑先把 \(s_1\) 变换为 \(\texttt{(())()()()()}\) 的形式,即对应的二叉树只有根节点有左儿子。

从二叉树右链的底端开始操作。

  1. 若无左儿子,说明当前子树已经是右链,考虑父节点。
  2. 若左儿子有左儿子,对当前点使用操作 1,此时右链长度增加 \(1\)。
  3. 若左儿子只有右儿子,对当前点使用操作 2,此时右链长度增加 \(1\)。
  4. 若左儿子为叶子节点且当前点不是根,对父节点使用操作 3,右链长度不变,而此时上图黄、蓝子树为空,故当前点(对应上图深蓝色节点)不再有左儿子。

操作 1、2 会使右链长度增加 \(1\),右链长度最多从 \(1\) 增加到 \(n - 1\),故操作 1、2 总次数不超过 \(n - 2\) 次。

操作 3 不会改变右链长度,对于右链上非根的点,最多作为深蓝色点操作 \(1\) 次,而右链有 \(n - 1\) 个点,故操作 3 次数不超过 \(n - 2\) 次。

最后删除根节点的左儿子,并在右链底端增加一个叶子节点。

然后把 \(s_2\) 用相反的操作变换为右链。从根开始,若存在左儿子,则对当前点反向使用操作 4;若不存在左儿子,则考虑右子树。

但如果直接这样做,当右链底存在左儿子时,无法反向使用操作 4,所以先在右链底端加一个节点,保证反向操作 4 总能进行。最后再删除这个点。

这时树上有 \(n + 1\) 个点,正向操作的初始状态右链长度为 \(n + 1\),每次操作 4 会使右链长度减少 \(1\),故操作 4 次数不超过 \(n\) 次。

总操作次数不超过 \(3n\),可以通过。

对于题目中要输出的 \(p\) 的长度,由于前四种操作的点都在右链上,操作点的子树对应括号序列的一段后缀,所以 \(p\) 的长度等于 \(2(m-size_x)\),\(m\) 为当前树上节点数,执行操作时需要维护子树大小。

参考代码(暂无数据,能通过样例,不保证正确性,欢迎 hack):

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;
int id, T, n, k, val[N];
char sa[N], sb[N];
int fa[N], ls[N], rs[N], siz[N], root;

inline void pushup(int x) {
	siz[x] = siz[ls[x]] + siz[rs[x]] + 1;
}

void dfs(int x) {
	if (ls[x]) dfs(ls[x]);
	if (rs[x]) dfs(rs[x]);
	pushup(x);
}

void build(char s[]) {
	root = 0;
	for (int i = 1; i <= n; ++i)
		fa[i] = ls[i] = rs[i] = siz[i] = 0;
	int cnt = 0;
	stack<int> st;
	for (int i = 1; i <= 2 * n; ++i) {
		if (s[i] == '(') {
			val[i] = ++cnt;
			st.push(cnt);
			if (i == 1) root = cnt;
			else {
				fa[cnt] = val[i - 1];
				if (s[i - 1] == '(') ls[val[i - 1]] = cnt;
				else rs[val[i - 1]] = cnt;
			}
		} else {
			val[i] = st.top();
			st.pop();
		}
	}
	dfs(root);
}

inline void rotateA(int z) {
	int y = ls[z], f = fa[z], c = rs[y];
	rs[y] = z, fa[z] = y;
	ls[z] = c, fa[c] = z;
	rs[f] = y, fa[y] = f;
	fa[0] = ls[0] = rs[0] = 0;
	if (!f) root = y;
	pushup(z);
	pushup(y);
}

inline void rotateB(int z) {
	int x = ls[z], y = rs[x], f = fa[z], b = ls[y], c = rs[y];
	ls[y] = x, fa[x] = y;
	rs[y] = z, fa[z] = y;
	rs[x] = b, fa[b] = x;
	ls[z] = c, fa[c] = z;
	rs[f] = y, fa[y] = f;
	fa[0] = ls[0] = rs[0] = 0;
	if (!f) root = y;
	pushup(x);
	pushup(z);
	pushup(y);
}

inline void rotateC(int x) {
	int z = rs[x], y = ls[z], f = fa[x], b = ls[y], c = rs[y];
	ls[y] = x, fa[x] = y;
	rs[y] = z, fa[z] = y;
	rs[x] = b, fa[b] = x;
	ls[z] = c, fa[c] = z;
	rs[f] = y, fa[y] = f;
	fa[0] = ls[0] = rs[0] = 0;
	if (!f) root = y;
	pushup(x);
	pushup(z);
	pushup(y);
}

inline void rotateD(int y) {
	int x = ls[y], f = fa[y], b = rs[x];
	rs[x] = y, fa[y] = x;
	ls[y] = b, fa[b] = y;
	rs[f] = x, fa[x] = f;
	fa[0] = ls[0] = rs[0] = 0;
	if (!f) root = x;
	pushup(y);
	pushup(x);
}

typedef pair<int, int> pii;
vector<pii> ans1, ans2;

void work(int x) {
	while (x) {
		while (ls[x]) {
			if (ls[ls[x]]) {
				ans1.emplace_back(1, 2 * (n - siz[x]));
				rotateA(x);
			} else if (rs[ls[x]]) {
				ans1.emplace_back(2, 2 * (n - siz[x]));
				rotateB(x);
			} else if (fa[x]) {
				ans1.emplace_back(3, 2 * (n - siz[fa[x]]));
				rotateC(fa[x]);
			} else {
				break;
			}
		}
		x = fa[x];
	}
}

void workB() {
	int x = root;
	while (x) {
		if (ls[x]) {
			ans2.emplace_back(4, 2 * (n + 1 - siz[x]));
			rotateD(x);
			x = fa[x];
		} else {
			x = rs[x];
		}
	}
}

inline void clear() {
	for (int i = 1; i <= 2 * n; ++i)
		val[i] = sa[i] = sb[i] = 0;
	for (int i = 1; i <= n + 1; ++i)
		fa[i] = ls[i] = rs[i] = siz[i] = 0;
	root = 0;
	ans1.clear();
	ans2.clear();
}

void solve() {
	cin >> n >> k >> (sa + 1) >> (sb + 1);
	build(sa);
	int R = root;
	while (rs[R]) R = rs[R];
	work(R);
	if (ls[root]) {
		ans1.emplace_back(6, 1);
		ans1.emplace_back(5, 2 * n - 2);
	}
	ans1.emplace_back(5, 2 * n);
	build(sb);
	R = root;
	while (rs[R]) R = rs[R];
	rs[R] = n + 1;
	fa[n + 1] = R;
	dfs(root);
	ans2.emplace_back(6, 2 * n);
	workB();
	reverse(ans2.begin(), ans2.end());
	cout << ans1.size() + ans2.size() << '\n';
	for (pii p : ans1) {
		int x, y;
		tie(x, y) = p;
		cout << x << ' ' << y << '\n';
	}
	for (pii p : ans2) {
		int x, y;
		tie(x, y) = p;
		cout << x << ' ' << y << '\n';
	}
	clear();
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> id >> T;
	while (T--) solve();
	return 0;
}

标签:洛谷,右链,rs,int,题解,back,WC2022,ls,操作
From: https://www.cnblogs.com/2ha-maomao-2006/p/solution-p8077.html

相关文章

  • 1.8模拟赛题解
    T1考虑每次反弹后,球的运动轨迹都会偏移\(2\beta\),总偏移量即为\(2k\beta\),而最后需要回到原点,因此\(360|2k\beta\),简单求\(\gcd\)即可。T2设\(ans_k\)表示出现过......
  • 1.11模拟赛题解
    T1对于方阵\(A\),考虑其反方阵\(A'\)。容易发现\(A\)与\(A'\)的权值和相同,而其中必有一个与\(B\)的差不超过\(\lfloor\frac{nm}{2}\rfloor\),因此判断一下哪个满足......
  • 1.9模拟赛题解
    T1从左到右扫描,首先如果\(a_i<b_i\)那么一定无解,否则不断在其右边找最近的\(j\)使得\(a_j\in[b_i,a_i]\),把\(a_i\)和\(a_j\)交换。感性理解这是对的。T2先证操......
  • 1.12模拟赛题解
    T1容易知道答案为原图的最大子二分图大小。枚举每个点在二分图的左边还是右边,计算出答案。时间复杂度\(O(2^n\timesm)\)。T2考虑递推构造方案。假设现在已经有了一组......
  • POI Excel格式报表生成 同步下载问题解决
    前言解决POI导出功能,过时方法和新增样式放在最下面或者参考下文POI样式调节0.maven(新版本)<poi.version>4.1.2</poi.version> <dependency> <groupId>org.ap......
  • P6751 [IOI2019]视觉程序 题解--zhengjun
    提供一种简介易懂的做法。首先曼哈顿距离的绝对值比较难处理,所以可以转成切比雪夫距离。具体地说,就是\((x,y)\)变成\((x+y,x-y)\)(接下来所述的坐标都是变换后的)。这......
  • YACS 2022年12月月赛 乙组 T1 拼接单词 题解
    一道结论题,代码相当的短。我们先来考虑会拼出重复的情况:那必定是第一个字符串里有一个$a$(其他的也行),第二个也有一个$a$。那么我们就可以选择拿第一个字符串$a$前面的......
  • YACS 2022年12月月赛 乙组 T2 八进制小数 题解
    纪念一下,两件事。$1.$打$YACS$一年了,时间过得好快啊。$2.$第一次$AK$乙组。高精板子。$8$进制转十进制,很简单。小数部分第一位的数字乘上$8^{-1}$,第二位就乘上......
  • LOJ #535 题解
    问题转化为交换两个数,使排列的逆序对数最少。设交换\(a_i\)和\(a_j\)且\(i<j,a_i>a_j\)。则减小的逆序对数为\[1+\sum_{k=i+1}^{j-1}[a_k<a_i]-[a_k>a_i]+[a_k>a_j]......
  • AT2282 [ABC051C] Back and Forth 题解
    Description在一个平面直角坐标系内,有一点\(A(x_1,y_1)\)和点\(B(x_2,y_2)\)你需要从\(A\)点走到\(B\)点,再走到\(A\)点,再走到\(B\)点,再回到\(A\)点。期间,你......