CSP2021 T3 回文

回文

  传送门:CSP2021 T3 回文

题目大意

  给定一个长度为 2 n 2n 2n 的数组,在这个数组中,数字 1 , 2 , 3 , ⋯   , n 1, 2, 3, \cdots, n 1,2,3,,n 分别出现两次。

  现在有两个操作,一是把 a a a 的开头放到 b b b 数组的结尾,二是把 a a a 的结尾放到 b b b 的结尾。要求使得 b b b 最后是一个回文数组,并且要求字典序最小。

  最后输出方案。

分析

  假设我们第一步是 L L L(第一步是 R R R 其实也差不多, 这里就以是 L L L 为例)。我们显然可以 O ( n ) O(n) O(n) 在数组中找到一个点 x x x 满足 x ∈ [ 2 , 2 n ] ∧ a 1 = a x x \in [2, 2n] \land a_1 = a_x x[2,2n]a1=ax。然后我们就可以发现,因为我们第一步把 a 1 a_1 a1 放到了 b b b 数组的开头,所以 b b b 数组的结尾就一定是我们找到的 a x a_x ax。所以我们就通过这两个点把数组分成了 2 2 2 个栈,分别是:

  1. 栈顶 → \to 栈底 : a 2 → a x − 1 a_2 \to a_{x - 1} a2ax1
  2. 栈顶 → \to 栈底 : a 2 n → a x + 1 a_{2n} \to a_{x + 1} a2nax+1

  我们就以样例中的第一组为例子:

4 , 1 , 2 , 4 , 5 , 3 , 1 , 2 , 3 , 5 \color{red}{4}\color{black}{, 1, 2,} \color{red}{4} \color{black}{, 5, 3, 1, 2, 3, 5} 4,1,2,4,5,3,1,2,3,5

  我们弄出来的两个栈就是:

在这里插入图片描述
  然后我们就可以继续下面的工作了 似乎变得很简单了qwq

  我们把这两个栈看成双端队列,然后找同时再栈底和栈顶都存在的数字(可能有两对或者一对,一对就直接搞,两队就选字典序小的搞)。同时弹出它们两个然后记录一下是怎么弹的输出就好了。

  如果没看懂上面的文字描述的可以看下面的完整的弹栈图解(还是以刚才的数列为例):

在这里插入图片描述
  显然这样做的复杂度是 O ( n ) O(n) O(n) 的。

代码

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 500500 << 1

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int T = 0;
int n = 0;
int a[MAXN] = { 0 };
char ans[MAXN];

bool work(int l1, int r1, int l2, int r2){
	for(int i = 1; i < n; i++){
		if(l1 <= r1 and ((l2 <= r2 and a[l1] == a[l2]) or (l1 < r1 and a[l1] == a[r1])))
			if(l1 < r1 and a[l1] == a[r1]){
				l1++, r1--;
				ans[i] = 'L', ans[2 * (n - 1) - i + 1] = 'L';
			}
			else{
				l1++, l2++;
				ans[i] = 'L', ans[2 * (n - 1) - i + 1] = 'R';
			}
		else if(l2 <= r2 and ((l1 <= r1 and a[r1] == a[r2]) or (l2 < r2 and a[l2] == a[r2])))
			if(l2 < r2 and a[l2] == a[r2]){
				l2++, r2--;
				ans[i] = 'R', ans[2 * (n - 1) - i + 1] = 'R';
			}
			else{
				r1--, r2--;
				ans[i] = 'R', ans[2 * (n - 1) - i + 1] = 'L';
			}
		else return 0;
	}
	return 1;
}

int main(){
	T = in;
	while(T--){
		n = in; int x1 = -1, x2 = -1;
		for(int i = 1; i <= 2 * n; i++) a[i] = in;
		for(int i = 1; i <= 2 * n; i++) ans[i] = 0;
		for(int i = 2; i <= 2 * n; i++) if(a[i] == a[1]) { x1 = i; break; }
		for(int i = 1; i < 2 * n; i++) if(a[i] == a[2 * n]) { x2 = i; break; }
		if(work(2, x1 - 1, x1 + 1, 2 * n)) printf("L%sL\n", ans + 1);
		else if(work(1, x2 - 1, x2 + 1, 2 * n - 1)) printf("R%sL\n", ans + 1);
		else puts("-1");
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值