Codeforces Round #673 (Div. 2) E. XOR Inverse

题目链接
题意:
给一个数组 ,需要找到一个 X ,使数组中每个数 异或 X 后 ,数组中的逆序对数最少 ,如果有多个 X 找最小的那个,求 X和逆序对数。

思路:
1、因为数的高位能直接接决定数值的大小,在当前位异或1并不能改变高位相同的这些数内部的逆序数对,所以我们处理时可以不用考虑整个数的大小,只需要看高位的逆序对数是否能通过异或1变得更小
2、同样的道理,当 当前位的前几位数确定且前几位不同时,改变当前位也不能改变这两个数的相对大小。所以我们只用计算每组相同高位的数内部的逆序数对和正序数对,看是否需要异或1。这就需要用到字典树了。
代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int inf = 0x3f3f3f3f;
const int N = 4000005;
const double eps = 1e-8;


int n,m;
int son[N][2],cnt[N],idx;
int a[N];
vector<int> q[N];
ll num[N][2];//num[i][0]第i为为0的逆序对的个数 
ll ans = 0;
int ans1 = 0;
void insert(int v,int id){
    
    
	int i,j;
	int p = 0;
	for(i = 30;i>=0;i--){
    
    
		int u = v>>i&1;
		if(son[p][u] == 0){
    
    
			son[p][u] = ++idx;
		}
		p = son[p][u];
		q[p].push_back(id);
		//插入该字典序位置的数 
	}
}

//遍历当前节点 ,当前是第v位 
void dfs(int u,int v){
    
    
	int i,j;
	if(son[u][0] && son[u][1]){
    
    
		vector<int> a1 = q[son[u][0]];
		vector<int> a2 = q[son[u][1]];
		ll res = 0;
		i = 0,j = 0;
		//求逆序对
		while(i < a1.size() && j < a2.size()){
    
    
			if (a1[i] <= a2[j]){
    
    
	        	i++;
			}else{
    
    
				res += a1.size()-i;
				j++;
			}
		}
		ll sum = (ll)a1.size()*a2.size();
		num[v-1][0] += res;
		num[v-1][1] += sum-res;
		dfs(son[u][0],v-1);
		dfs(son[u][1],v-1);
	}else if(son[u][0]){
    
    
		//如果左儿子存在
		dfs(son[u][0],v-1); 
	}else if(son[u][1]){
    
    
		//如果右儿子存在 
		dfs(son[u][1],v-1);
	}
	return ;
}

int main(){
    
    
	int i,j;
	cin>>n;
	for(i = 1;i<=n;i++){
    
    
		cin>>a[i];
		insert(a[i],i);
	}
	dfs(0,31);
	
	for(i = 30;i>=0;i--){
    
    
		if(num[i][0] > num[i][1]){
    
    
			ans1 = (1<<i)^ans1;
		}
		ans += min(num[i][0],num[i][1]);
	}
	cout<<ans<<" "<<ans1<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44630656/article/details/109537603