Codeforces Round #826 (Div. 3)
题意: 给你给你一个高度为n的完全二叉树(包含的子节点为2^n),然后给你n个数分别对应子树
的n个结点,每次可以进行一个操作:交换树的任何非叶顶点,并交换它的左右子结点(以及它们的
子树)。 问最后组成的序列能不能形成一个递增的序列,如果能就输出最少的操作数,不能
就输出-1。
思路:因为最后要组成递增的序列,所以如果我的子树不是单调递增的话,那么我的整个区间就
不是递增的,所以我们要从子树往上不断更新成单调递增的序列。
然后我们每次修改都是从子区间往上更新。
如果子区间不是递增的那么整个序列也不可能是递增的,那么我们要从子树开始不断向上交换,
如果区间长度为l,r那么左区间的最大的下标是mid,所以更新的值也只能是swap(i,i+(r-l+1>>1)); ,r-l+1是一个半区的长度
对子区间进行考虑:如果当前节点的两个子区间不是递增的话(s[l] > s[r] ),即左子树的第一个节点大于右子树的第一个节点,就要进行交换,不断dfs交换排序(归并排序好像是有点像),如果排序后的子序列是递增的就输出需要的次数,否则输出-1。
#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define ll long long
using namespace std;
const int N=1000000+100;
int n ,m,h;
ll s[N];
int cnt;
void dfs(int l,int r )//要修改的左右区间,首先肯定是从子区间不断
{
if(l==r)return ;
int mid=l+r>>1;//左半区间的的端点
int len=r-l+1>>1;//要更新的长度
dfs(l,mid),dfs(mid+1,r);//继续递归子区间image.png
if(s[l]>s[r])//如果修改后的字区间不满足递增
{
cnt++;
for(int i =l;i<=mid;i++)//对左半边的区间进行排序
swap(s[i],s[i+len]);
}
}
/*
因为最后要组成递增的序列,然后我们每次修改都是从子区间往上更新。
如果子区间不是递增的那么整个序列也不可能是递增的,那么我们要从子树开始不断向上交换,
如果区间长度为l,r那么左区间的最大的下标是mid,所以更新的值也只能是swap(i,i+(r-l+1>>1)); ,r-l+1是一个半区的长度
*/
int main()
{
int t;
cin>>t;
while(t--)
{
cnt=0;
cin>>n;
for(int i =1;i<=n;i++)cin>>s[i];
dfs(1,n);
int flag=0;
for(int i =1;i<=n;i++)
{
if(s[i]>s[i+1]&&i+1<=n)
flag=1;
}
if(flag)cout<<"-1\n";
else cout<<cnt<<endl;
}
return 0;
}