codeforces round #673(div2)E题———01字典树求逆序对数量

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;
}

猜你喜欢

转载自blog.csdn.net/Stevenwuxu/article/details/109314935
今日推荐