CF1741D 题解
思路
这题中我们只能对任意一个根节点的两棵子树进行整体交换,而不是整体翻转。而每两个子树交换两次就会变回原先的样子,所以我们对这两棵子树最多只会交换一次,即要么不交换,要么只交换一次。我们只要用类似归并排序的方式枚举每一对子树的父节点,然后按以下步骤进行递归:
- 拆分两个子树进行递归;
- 如果两个子树中有不合法的,则这整棵树肯定也不合法,提前返回不合法;
- 分情况考虑:
- 如果当前整个区间已经有序,即左子树区间的最后一个元素比右子树区间的第一个元素还小,则说明合法,提前返回合法;
- 如果左子树和右子树调换后有序,即左子树区间的第一个元素比右子树的最后一个元素还大,则也说明合法,但是要调换过,所以我们要先对两个子树进行调换,然后对记录交换次数的答案 加一,也返回合法;
- 其它情况时,不交换也不合法,交换了也不合法,就是真的不合法了,返回不合法。
到此,整个递归流程就完成了。
代码
# 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;
}

浙公网安备 33010602011771号