逆序对
逆序对非常常见,有三种求解的方法,效率差不多,但是树状数组法较快。
归并排序
归并排序的思想就是递归分治,把要解决的区间分成两个区间比较(a_i)和(a_j)的大小(其中(a_i)属于左区间,(a_j)属于右区间,其实就是将左右区间合并、并排序),若(a_i<a_j),则将(a_i)复制到(r_k)中,然后将(i)和(k)都加(11),否则将(a_j)复制到(r_k)中,将(j,k)加(11),最后再将(r_k)移动到(a_i)中,然后继续合并;
void msort(int l, int r)
{
if(l == r)
return;
int mid = (l + r) / 2;
msort(l, mid);
msort(mid + 1, r);
int i = l;
int j = mid + 1;
int k = l;
while (i <= mid && j <= r)
{
if (a[i] <= a[j])
{
fu[k] = a[i];
k++; i++;
}
else
{
fu[k] = a[j];
k++;
j++;
ans += mid - i + 1;
}
}
while (i <= mid)
{
fu[k] = a[i];
k++;
i++;
}
while (j <= r)
{
fu[k] = a[j];
k++;
j++;
}
for (int i = l; i <= r; i++)
a[i] = fu[i];
}
树状数组
树状数组其实就是桶的思想进行一波优化所得出来的。每拿到一个值放入桶中,统计所有大于这个数的数量,然后累加,发现这正好可以用树状数组优化。当然在使用之前还是应该离散化一下,不然桶会炸的。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define lowbit(n) (n & (-n))
#define int long long
using namespace std;
int n, sum;
int a[1001000], b[1010000], tree[1000100];
void add(int x) {
while (x <= n) {
tree[x]++;
x += lowbit(x);
}
}
int query(int x) {
int ans = 0;
while (x > 0) {
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
bool cmp(const int &x, const int &y)
{
if(b[x]==b[y]) return x > y;
return b[x] > b[y];
}
signed main()
{
scanf("%lld", &n);
for(register int i = 1; i <= n; i++)
{
scanf("%lld", &b[i]);
a[i] = i;
}
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++)
{
add(a[i]);
sum += query(a[i] - 1);
}
printf("%lld", sum);
}
线段树
一般的线段树并不能解决这个问题,而且需要用权值线段树来解决这种问题。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls left, mid, root << 1
#define rs mid + 1, right, root << 1 | 1
#define int long long
using namespace std;
int data[1010000], ans = 0;
struct ha {
int num, id;
}a[1001000];
struct t {
int l, r, sum;
}tree[2001000];
bool cmp(ha a, ha b)
{
return a.num < b.num;
}
inline void build(int left, int right, int root)
{
tree[root].l = left;
tree[root].r = right;
if (left == right) return;
int mid = (left + right) >> 1;
build(ls), build(rs);
}
inline void pushup(int root)
{
tree[root].sum = tree[root << 1].sum + tree[root << 1 | 1].sum;
}
void update(int root, int add)
{
if (tree[root].l == tree[root].r)
{
tree[root].sum ++;
return;
}
int mid = (tree[root].l + tree[root].r) >> 1;
if (add <= mid)
update(root << 1, add);
else
update(root << 1 | 1, add);
pushup(root);
return;
}
int query(int root, int a)
{
if (tree[root].l == tree[root].r)
return tree[root].sum;
int mid = (tree[root].l + tree[root].r) >> 1;
if (a <= mid)
return tree[root << 1 | 1].sum + query(root << 1, a);
else return query(root << 1 | 1, a);
}
signed main()
{
int n;
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &data[i]), a[i].id = i, a[i].num = data[i];
sort(a + 1, a + 1 + n, cmp);//
for (int i = 1, j = 0; i <= n; i++)
{
if (a[i].num != a[i - 1].num || i == 1) j++;
data[a[i].id] = j;
}
//此处向上为离散化。
build(1, n, 1);
for (int i = 1; i <= n; i++)
{
ans += query(1, data[i] + 1);//要算比data[i]严格大于的数。
update(1, data[i]);
}
printf("%lld", ans);
return 0;
}