名叫玛莎的女孩在森林里散步时发现了一棵完整的二叉树,树高 n n n ,长度为 m = 2 n m=2n m=2n 的排列组合 p p p 。
一棵高度为 n n n 的完整二叉树是一棵有根的树,它的每个顶点(叶子除外)都正好有两个子顶点,并且从树根到任何一个叶子的路长度都是 n n n 。下图是 n = 2 n=2 n=2 的完整二叉树。
排列是由 n n n 个不同的整数组成的数组,从 1 1 1 到 n n n 。例如, [ 2 , 3 , 1 , 5 , 4 ] [ 2,3,1,5,4 ] [2,3,1,5,4] 是一个排列,但是 [ 1 , 2 , 2 ] [ 1,2,2 ] [1,2,2] 不是( 2 2 2 出现了两次), [ 1 , 3 , 4 ] [ 1,3,4 ] [1,3,4] 也不是一个排列 ( n = 3 ( n=3 (n=3 ,但是数组中有 4 ) 4 ) 4)。
让我们从左到右枚举这棵树的 m 片叶子。编号为 i 的叶子包含值 pi ( 1≤i≤m )。( 1≤i≤m ).
例如,如果 n=2 , p=[3,1,4,2] ,这棵树就会变成这样:
如果树叶中的值从左到右依次递增,Masha 就会认为这棵树很漂亮。
在一次操作中,Masha 可以选择树中任何一个非叶顶点,交换它的左右子树(及其子树)。
例如,如果 Masha 对上面讨论的树的根进行这一操作,它的形式将如下:
帮助玛莎了解她是否能通过一定数量的操作使一棵树变得美丽。如果可以,那么输出使树变美所需的最少操作数。
输入
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 4 ) t ( 1≤t≤10^4 ) t(1≤t≤104) —— 测试用例数。
在每个测试用例中,第一行包含一个整数 m ( 1 ≤ m ≤ 262144 ) m ( 1≤m≤262144 ) m(1≤m≤262144),该整数是 2 2 2 的幂次,即排列的大小 p p p 。
第二行包含 m m m 个整数: p 1 , p 2 , … , p m ( 1 ≤ p i ≤ m ) p_1,p_2,…,p_m ( 1≤p_i≤m ) p1,p2,…,pm(1≤pi≤m) —— 排列 p p p 。
保证所有测试用例中 m m m 的总和不超过 3 ⋅ 1 0 5 3⋅10^5 3⋅105 。
输出
对于每个测试用例,另起一行打印 Masha 能让树变得漂亮的最小操作数,如果不能,则打印 -1
。
二叉树由于其性质与分治的思想嵌合较好,故经常两个东西一块考。
对于这道题操作的时候,根据分治的思想,我们就把二叉树每一层都进行一次分段的操作,比如在根节点的时候,我们可以分为左子树和右子树,这时候去看两段的叶节点(p数组)的值,如果左子树的最大值大于了右子树的最大值,那么就需要左右子树进行交换。
然后我们就可以不断重复这个过程,遍历完整个二叉树。
如果最后p数组可以被排序好,那么就说明得到了解,如果没有就输出-1.
CODE:
#include<bits/stdc++.h>
using namespace std;
const int N = 262144 + 10;
int p[N];
int dfs(int l,int r){
if(l == r)return 0;
int res = 0;
int mid = l + r >> 1;
int maxL = *max_element(p+l,p+mid+1);
int maxR = *max_element(p+mid+1,p+r+1);
if(maxL > maxR){
res++;
for(int i = l;i <= mid;i++){ //交换左右子树操作
swap(p[i],p[mid + i - l + 1]);
}
}
return dfs(l,mid) + dfs(mid + 1,r) + res; //继续搜当前节点的左子树和右子树
}
void solve(){
int n;cin >> n;
for(int i = 1;i <= n;i++)cin >> p[i];
int res = dfs(1,n);
if(is_sorted(p+1,p+1+n))cout << res << endl;
else cout << -1 << endl;
}
int main(){
int T;cin >> T;
while(T--){
solve();
}
return 0;
}
分治的题目接触较少,思想还未掌握,需要练习。