XOR Inverse
题目传送门
XOR Inverse
题目大意:
给你一个n个数的数组a,要你求得一个最小的x,使得x异或每个a[i]之后得到的数组逆序对最少。输出x的值,和逆序对的数量。
思路:
补了这道题,我学到了原来01字典树还能用来求逆序对的数量(能做出来的人真是tql)。
AC Code
//先用字典树维护二进制数,并且每个节点存着该节点的编号。从高到低位选取逆序对小的
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e6+10;
int tries[N][3];
vector<int>v[N];
int n,tot=0;
LL cnt[35][3];
void insert(LL num,int idx)
{
//将该数加入到字典树中
int p=0,k;
for(int i=30;i>=0;i--)
{
//从低位到高位加入
k=(num>>i)&1;
if(tries[p][k]==0) tries[p][k]=++tot;
p=tries[p][k];
v[p].push_back(idx); //将该数的编号加入
}
}
void dfs(int idx,int pos)
{
int l=tries[idx][0];//接下去一位是0
int r=tries[idx][1];//接下去一位是1
if(l) dfs(l,pos-1);
if(r) dfs(r,pos-1);
if(!tries[idx][0]||!tries[idx][1]) return ;
LL k=0,ans=0;
for(int i=0;i<v[l].size();i++)
{
while(k<v[r].size()&&v[r][k]<v[l][i]) k++;
ans=ans+k;
}
cnt[pos][0]+=ans;
cnt[pos][1]+=(LL)v[l].size()*v[r].size()-ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
LL num;
scanf("%lld",&num);
insert(num,i);
}
dfs(0,30);
LL res=0,x=0;
for(int i=0;i<=30;i++)
{
if(cnt[i][0]<=cnt[i][1])
{
res=res+cnt[i][0];
}
else
{
res+=cnt[i][1];
x=x+(1<<i);
}
}
printf("%lld %lld\n",res,x);
//system("pause");
return 0;
}