Codeforces1416 C. XOR Inverse(逆序对,异或,按二进制位考虑,字典树递归写法)

题意:

在这里插入图片描述

解法:
因为是异或,显然往二进制考虑.

考虑x的二进制从高位起第i位为1会有什么影响:
对于[1,i-1]相同,第i位不同的数,
这样的数是通过第i位的0,1进行比较的,
如果x为1,会导致这样的数形成的顺、逆序对数量变换.

那么我们统计每个位上正序对和逆序对的数量,
如果逆序对>顺序对,那么x=1,因为交换会使得逆序对数量变少.

如何统计每个位上顺序对和逆序对数量呢,
01用字典树做:
开sz[node]表示node下有多少个数,
rev[node]表示当前节点决定的逆序对数量,
sum[node]表示当前节点决定的顺序对数量.

如何维护rev[node]和sum[node]:
每次插入一个数,如果当前位为0,
那么rev[node]+=sz[a[node][1]].
如果当前位为1,那么sum[node]+=sz[a[node][0]].

设s[i]为第i位上的顺序对数量和,
r[i]为第i位上的逆序对数量和,
s[]和r[]可以通过dfs字典树得到.

对于每一位i,如果r[i]>s[i],那么x|=(1<<i),即交换,
否则不交换.
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=3e5+5;
int a[maxm];
int r[maxm];
int s[maxm];
int n;
int cal(int x){
    return x*(x-1)/2;
}
struct Trie{
    int a[maxm*30][2],tot=1;
    int sz[maxm*30];
    int sum[maxm*30];//节点的顺序对数量
    int rev[maxm*30];//节点的逆序对数量
    void add(int x,int i,int node){
        if(i==-1){
            sz[node]++;
            return ;
        }
        int v=(x>>i&1);
        //维护节点顺序对数量和逆序对数量.
        if(v==0)rev[node]+=sz[a[node][1]];
        else sum[node]+=sz[a[node][0]];
        //
        if(!a[node][v])a[node][v]=++tot;
        add(x,i-1,a[node][v]);
        sz[node]=sz[a[node][0]]+sz[a[node][1]];
    }
    void dfs(int node,int i){
        if(!node||i==-1)return ;
        r[i]+=rev[node];
        s[i]+=sum[node];
        dfs(a[node][0],i-1);
        dfs(a[node][1],i-1);
    }
}T;
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        T.add(a[i],30,1);
    }
    T.dfs(1,30);
    int ans=0,x=0;
    for(int i=30;i>=0;i--){
        if(r[i]>s[i]){
            x|=(1<<i);
            ans+=s[i];
        }else{
            ans+=r[i];
        }
    }
    cout<<ans<<' '<<x<<endl;
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    solve();
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值