https://codeforces.com/contest/1416/problem/C
傻逼题想不出来
这题关键是要想到字典树上可以每个节点都存下经过的点的坐标,这样就存了30*n个数是不会爆内存的,艹没想到这个头都想裂了
接下来就分析每一层选哪个0和1会新增多少逆序对就好了
然后出来以后分位取min就行了,没有后效性
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=3e5+10;
int n,tot;ll mini,ans;
int a[maxl];
ll dp[31][2];
int tr[maxl*31][2];
vector<int> b[maxl*31];
inline void add(int x,int id)
{
int u=0,c;
for(int i=30;i>=0;i--)
{
c=x>>i&1;
if(!tr[u][c])
tr[u][c]=++tot;
u=tr[u][c];
b[u].push_back(id);
}
}
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
add(a[i],i);
}
}
inline void dfs(int u,int mi)
{
int ls=tr[u][0],rs=tr[u][1];
if(ls)
dfs(ls,mi-1);
if(rs)
dfs(rs,mi-1);
if(!ls || !rs)
return;
ll d=0,ind=0,llen=b[ls].size(),rlen=b[rs].size();
for(int lx:b[ls])
{
while(ind<rlen && b[rs][ind]<lx)
++ind;
d+=ind;
}
dp[mi][0]+=d;
dp[mi][1]+=llen*rlen-d;
}
inline void mainwork()
{
dfs(0,30);mini=0;ans=0;
for(int i=30;i>=0;i--)
if(dp[i][0]<=dp[i][1])
mini+=dp[i][0];
else
mini+=dp[i][1],ans|=(1<<i);
}
inline void print()
{
printf("%lld %lld",mini,ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}