Codeforces Round #673 (Div. 2) 全文见:https://blog.youkuaiyun.com/qq_43461168/article/details/112606806
E. XOR Inverse
题意:一个数组。要找到一个x。使得数组中每个数都异或x之后。数组中的逆序对数量最少。
思路:这种一般就是贪心构造。应且又和异或有关。那肯定和字典树有关。就往这个方向去想。首先用字典树,可以求出数组中逆序对的数了。dp[i][j] 表示第i位上 如果为j。存在的逆序对数量。考虑第i位上的0和1两颗子树。如果现在插进去的是0。那么逆序对是不是增加了 sz[i][1]。并且这个逆序对数量是当i位为1时产生的。所以 dp[i][1] += sz[i][1]。同理插入0时。 dp[i][0] += sz[i][0]。然后在构造x的时候。肯定选择逆序对更少的。 如果 dp[i][1] > dp[i][0] 那么就要让这一位变成0。这样就留下了 dp[i][0] 所以答案加上 (1<<i)
AC代码:
#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 5e6+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int sz[N];
int tree[N][2];
int tot = 0;
int dp[N][2];
void insert(int x){
int pos = 0;
for(int i = 30 ; i >= 0 ; i --){
int tmp = (x >> i ) & 1;
if(!tree[pos][tmp]) tree[pos][tmp] = ++tot;
dp[i][tmp^1] += sz[tree[pos][tmp^1]];
pos = tree[pos][tmp];
sz[pos] ++;
}
}
signed main(){
cin>>n;
for(int i = 0 ; i < n; i ++){
int x;cin>>x;
insert(x);
}
int res = 0,cnt = 0;;
for(int i = 30 ; i >= 0 ; i--){
if(dp[i][1] > dp[i][0]) res += (1LL<<i),cnt += dp[i][0];
else cnt += dp[i][1];
}
cout<<cnt<<" "<<res<<endl;
return 0;
}