*题解:Gym105465F Fast XORting

题目链接

解析

首先不难发现操作 2 至多进行 \(1\) 次,因为全体异或 \(x_1\) 之后全体异或 \(x_2\),相当于全体异或 \(x_1 \operatorname{xor} x_2\)。考虑在最后进行操作 \(2\),那么我们就需要计算至少通过多少次操作 1 来达到某种目标排列,使得排列异或上某个 \(x\) 后排列单调递增。但由于目标排列有 \(O(n)\) 个,所以不可行。

考虑在最开始进行操作 2,那么接下来只需计算要交换多少次才能使序列单调递增,由于每次交换都能减少一个逆序对,所以只需求逆序对个数。

考虑异或对两数大小关系的影响。可以发现,对于两数二进制从高往低第一个不同的位,若异或上的数 \(x\) 的该位为 \(1\),则异或后两数大小关系对调,否则不变。故可以贪心地从高往低位确定 \(x\),对于每一位,判断将该位设为 \(1\) 能否使得逆序对个数变少。考虑对于每个二进制位,对每个这一位上为 \(b\) 的数求在这之前有多少个高位相同,该位为 \(b \operatorname{xor} 1\) 的数。\(b=0\) 时对逆序对个数做负贡献,\(b=1\) 时做正贡献。如果最终贡献之和为负,则将该位设为 \(1\),否则设为 \(0\)。个数可以用 01Trie 来求,但实际上可以更优:维护 \(t_i\) 表示到目前位置高若干位为 \(i\) 的数字个数即可,详见代码。

求出 \(x\) 后进行异或并统计逆序对个数即可。时间复杂度 \(O(n \log n)\)

代码

/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
#define mid ((l + r) >> 1)
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
using namespace std;
typedef long long ll;
typedef unsigned ui;
typedef pair<ll, ll> pii;
const int N = (1 << 18) + 5, M = 20, P = 450, mod = 1e9 + 7, mod2 = 1e9 + 7, b1 = 131;
int a[N],b[N],t[N];
void add(int x,int k){
	for(;x < N;x += x & -x){
		b[x] += k;
	}
}
int ask(int x){
	if(x < 0) return 0;
	int res = 0;
	for(;x;x -= x & -x){
		res += b[x];
	}
	return res;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out1.txt","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int x = 0;
	for(int i=17;i>=0;i--){
		ll cnt = 0;
		for(int j=1;j<=n;j++){
			if(a[j] & (1 << i))
				cnt += t[(a[j] >> i) ^ 1];
			else
				cnt -= t[(a[j] >> i) ^ 1];
			t[a[j] >> i]++;
		}
		for(int j=1;j<=n;j++){
			t[a[j] >> i]--;
		}
		if(cnt < 0){
			x |= 1 << i;
		}
	}
	ll res = x != 0;
	for(int i=n;i>=1;i--){	 
		res += ask((a[i] ^ x));
		add((a[i] ^ x) + 1,1);
	}

	cout<<res;
    return 0;
}
posted @ 2026-07-30 22:29  yutar  阅读(3)  评论(0)    收藏  举报