题目链接
题意:
给一个数组 ,需要找到一个 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;
}