Codeforces 1417 E. XOR Inverse(字典树)

传送门

题意:

给出一个长度为 n n n的数组 a a a ,求一个最小的 x x x,使得 b [ i ] = a [ i ] ⨁ x b[i]=a[i] \bigoplus x b[i]=a[i]x ,且数组 b b b的逆序对数量最少。

题解:

比较两个数的大小,可以将其变为二进制,从高位往低位依次比较,直至遇到该位上的数不同。

那么我们可以从前往后依次向字典树中插入每一个元素,插入数的过程中,从高位到低位依次考虑。

假设当前是第 i i i 位,如果插入的数在 i i i 位为 0 0 0 , 那么该位贡献的逆序对数为 t r e e [ r o o t ] [ 1 ] tree[root][1] tree[root][1] 的大小,反之如果为 1 1 1

该位贡献的正序对数为 t r e e [ i ] [ 0 ] tree[i][0] tree[i][0] 的大小。

d p [ i ] [ 0 ] dp[i][0] dp[i][0] 表示二进制第 i i i 位正序对的个数, d p [ i ] [ 1 ] dp[i][1] dp[i][1] 表示二进制第 i i i 位逆序对的个数。

那么从高到低依次考虑 x x x的每一位,如果 d p [ i ] [ 0 ] < d p [ i ] [ 1 ] dp[i][0]<dp[i][1] dp[i][0]<dp[i][1] ,说明该位为 1 1 1 可以使得逆序对的数量更少,反之则为 0 0 0

代码:

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=3e5+5;
const int inf=0x3f3f3f3f;
int tree[MAXN * 20][2];
ll num[MAXN * 20], dp[50][2];
int a[MAXN];
int cnt = 0;
void insert(int x)
{
    int root = 0;
    for (int i = 30; i >= 0;i--){
        int id = ((x >> i) & 1);
        if(!tree[root][id])
            tree[root][id] = ++cnt;
        if(id==0&&tree[root][1])
            dp[i][1] += num[tree[root][1]];
        else if(id==1&&tree[root][0]){
            dp[i][0] += num[tree[root][0]];
        }
        root = tree[root][id];
        num[root]++;
    }
}
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n;i++){
        cin >> a[i];
    }
    for (int i = 1; i <= n;i++){
        insert(a[i]);
    }
    ll sum = 0, ans = 0;
    for (int i = 30; i >=0;i--){
        if(dp[i][0]<dp[i][1]){
            sum += dp[i][0];
            ans += (1 << i);
        }
        else{
            sum += dp[i][1];
        }
    }
    cout << sum << " " << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值