最接近神的人 树状数组(离散化)-逆序对
题目
破解了符文之语,小FF开启了通往地下的道路。当他走到最底层时,发现正前方有一扇巨石门,门上雕刻着一幅古代人进行某种活动的图案。而石门上方用古代文写着“神的殿堂”。小FF猜想里面应该就有王室的遗产了。但现在的问题是如何打开这扇门……
仔细研究后,他发现门上的图案大概是说:古代人认为只有智者才是最容易接近神明的。而最聪明的人往往通过一种仪式选拔出来。仪式大概是指,即将隐退的智者为他的候选人写下一串无序的数字,并让他们进行一种操作,即交换序列中相邻的两个元素。而用最少的交换次数使原序列变成不下降序列的人即是下一任智者。
小FF发现门上同样有着n个数字。于是他认为打开这扇门的秘诀就是找到让这个序列变成不下降序列所需要的最小次数。但小FF不会……只好又找到了你,并答应事成之后与你三七分……
输入格式:
第一行为一个整数n,表示序列长度
第二行为n个整数,表示序列中每个元素。
输出格式:
一个整数an**s,即最少操作次数。
输入样例:
4 2 8 0 3
输出样例:
3
说明/提示:
对于30的数据1≤n≤104。
对于100的数据1≤n≤5∗105;
−maxlongin**t≤A[i]≤maxlongin**t。
样例说明:开始序列为2803,目标序列为0238,可进行三次操作的目标序列:
1.Swap (8,0):2 0 8 3 2.Swap (2,0):0 2 8 3 3.Swap (8,3):0 2 3 8
Code Size Limit
16 KB
Java (javac)
Time Limit
2000 ms
Memory Limit
128 MB
Python (python3)
Time Limit
2000 ms
Memory Limit
128 MB
Other Compilers
Time Limit
1000 ms
Memory Limit
64 MB
思路
求最小交换数量即求逆序对数, 证明(贪心法):
对于一个序列我们以\(i > j \space and \space a_j < a_i\) 为逆序对,我们考虑如何最小交换, 先交换最大的数,这样能够保证不产生新的逆序对数,譬如序列\(4, 5, 9, 1, 3, 2\) 和9产生的逆序对数为\(3\)(当然也是9离末尾的距离)所以直接swap 3次,我们再以5类推,5产生的也是3对,直接移动三次, 一次类推, (当然更严谨的也可以用数学归纳法证明), 我们保证每次这样操作 swap途中和结束都不会产生新的逆序对,所以最小交换次数即为逆序对数
Code 树状数组求逆序对
#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
using i64 = long long;
const int N = 5e5 + 10;
i64 a[N], b[N];
int n;
std::unordered_map<i64, int> pos;
int tr[N];
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) {
tr[i] += c;
}
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
int main() {
std::cin >> n;
for (int i = 1; i <= n; i ++) {
std::cin >> a[i];
b[i] = a[i];
}
std::sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i ++) {
pos[b[i]] = i;
}
i64 ans = 0;
for (int i = 1; i <= n; i ++) {
ans += query(n) - query(pos[a[i]]);
add(pos[a[i]], 1);
}
std::cout << ans;
}