*题解: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;
}

浙公网安备 33010602011771号