CF1741D 题解

洛谷传送门 & CF 传送门

思路

这题中我们只能对任意一个根节点的两棵子树进行整体交换,而不是整体翻转。而每两个子树交换两次就会变回原先的样子,所以我们对这两棵子树最多只会交换一次,即要么不交换,要么只交换一次。我们只要用类似归并排序的方式枚举每一对子树的父节点,然后按以下步骤进行递归:

  1. 拆分两个子树进行递归;
  2. 如果两个子树中有不合法的,则这整棵树肯定也不合法,提前返回不合法;
  3. 分情况考虑:
    1. 如果当前整个区间已经有序,即左子树区间的最后一个元素比右子树区间的第一个元素还小,则说明合法,提前返回合法;
    2. 如果左子树和右子树调换后有序,即左子树区间的第一个元素比右子树的最后一个元素还大,则也说明合法,但是要调换过,所以我们要先对两个子树进行调换,然后对记录交换次数的答案 ansans 加一,也返回合法;
    3. 其它情况时,不交换也不合法,交换了也不合法,就是真的不合法了,返回不合法。

到此,整个递归流程就完成了。

代码

# include <bits/stdc++.h>
using namespace std;
int t, n, a[262150], ans;
void merge (int& l, int& mid) { //交换
	int i = l, j = mid + 1;
	while (i <= mid)
		swap (a[i], a[j]), ++ i, ++ j;
	return ;
}
bool stablesort (int l, int r) {
	if (l >= r) //只有一个元素,当然合法
		return 0;
	int mid = (l + r) / 2; //两个子树的分界点,l 到 mid 是左子树,mid+1 到 r 是右子树
	if (stablesort (l, mid) || stablesort (mid + 1, r)) //子树都不合法
		return 1;
	if (a[mid] < a[mid + 1]) //不要交换
		return 0;
	else if (a[l] > a[r]) //要交换
		merge (l, mid), ++ ans;
	else //不合法
		return 1;
	return 0;
}
int main () {
	cin >> t;
	while (t --) {
		cin >> n;
		ans = 0; //多组数据要清空
		for (int i = 0; i < n; ++ i)
			cin >> a[i];
		if (stablesort (0, n - 1)) //这里我其实故意搞反了,1 表示不合法,0 表示合法
			cout << "-1\n";
		else
			cout << ans << '\n';
	}
	return 0;
}
posted @ 2023-09-01 17:52  Vitamin_B  阅读(5)  评论(0)    收藏  举报  来源