CF1417E XOR Inverse(字典树上分治)

题意:

给出一个数组,询问最小的数x,使得数组里每个数异或这个x后,整个数组的逆序对数量最少。

题解:

/*
 *题意:
 *给出一个数组,询问一个最小的值x,使得这个数组的所有元素异或x后逆序对最少
 *考虑贪心做法,从低位到高位构造x
 *每次比较x的第i位是1和x的第i位是0所形成的数组的逆序对数量,选择少的那一个
 *可以将每个数的二进制形式高位到低位存到一个字典树里
 *两个数的大小取决于他们二进制位上第一个不同的位
 *可以单独考虑字典树的其中一个子树,设它的左子树为l,右子树为r,那么l里面的所有数一定是比r小的
 *可以枚举l里面有多少数的下标比r里面的数的下标小,这样一个子树内的逆序对数量就统计完了
 *然后将所有子树以此合并,最后根节点得到的是所有逆序数的数量
 
 *接下来考虑贪心构造x的这个过程
 *假设当前贪心到x的第i位,这一位是1,那么对应层数的子树要反转
 *开一个dp数组分别统计翻转和不翻转的逆序对数量
 *就完成了这个贪心的过程
 */ 
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e6+100;
typedef long long ll;
int n,Trie[maxn][2],tot;
ll dp[maxn][2];
vector<int> g[maxn];
void add (int x,int p) {
    int u=0;
    for (int i=29;i>=0;i--) {
        int bit=((x>>i)&1);
        if (!Trie[u][bit]) Trie[u][bit]=++tot;
        u=Trie[u][bit];
        g[u].push_back(p);
    }
}
void dfs (int u,int dep) {
    if (Trie[u][0]) dfs(Trie[u][0],dep-1);
    if (Trie[u][1]) dfs(Trie[u][1],dep-1);
    if (!Trie[u][0]||!Trie[u][1]) return;
    ll sum=0;
    int tt=0;
    for (int i=0;i<g[Trie[u][0]].size();i++) {
        while (tt<g[Trie[u][1]].size()&&g[Trie[u][1]][tt]<g[Trie[u][0]][i]) tt++;
        sum+=tt;
    } 
    dp[dep][0]+=sum;
    dp[dep][1]+=(ll)g[Trie[u][0]].size()*g[Trie[u][1]].size()-sum;
}
void solve () {
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {
        int x;
        scanf("%d",&x);
        add(x,i);
    }
    dfs(0,29);
    ll ans=0,u=0;
    for (int i=0;i<=29;i++) {
        ans+=min(dp[i][0],dp[i][1]);
        if (dp[i][1]<dp[i][0]) u+=(1<<i);
    }
    printf("%lld %lld
",ans,u);
}
int main () {solve();}
原文地址:https://www.cnblogs.com/zhanglichen/p/13744193.html