树状数组求逆序对
树状数组可以进行Point Update Interval Query 点更新,段求和的操作。同时也可以进行求逆序对。
首先要先了解树状数组的概念,树状数组是一种数据结构,具体的讲解我不写了,这个不是今天的重点 具体讲解
- 步入正题
在使用树状数组求逆序对中,树状数组的背景含义将有所变化。 以a[]数组表示我们要存储的一个数列,tree[]表示树状数组。初始化我们把tree数组全部置为 0 。表示序列a中的数我们目前一个都还没有插入到树状数组中。当我们执行add(a[i],1); 表示我把a[i],这个数据插入到了树状数组中去了。add参数是1表示插入。这样的好处是我们在统计小于a[i]的数有多少个时,直接使用query(a[i])就可以知道了。为什么这样呢?因为有一个比a[i]小我们加1,不相当于在树状数组中插入1吗。然后我们执行 sum+=i-query(a[i]); sum代表逆序对的个数计数。根据逆序对的定义我们不难理解为什么a[i]的逆序对是 i-query(a[i])。
因为只有a[i]>a[j] && i<j 这样才叫逆序对。 - 我这里贴的是hdu1394 这道题中还有个结论
这里我们谈到的a[i]都是1–n之间的连续的n个数,当数列中的数不再是连续的1–n之间的数时,我们就要采用离散化的手段,记录数在数列中的位置
/*
* Author: looooop
* Created Time: 2019/3/15 17:28:38
* File Name: MinimumInversionNumber.cpp
*/
#include "bits/stdc++.h"
using namespace std;
#define lson 2*i
#define rson 2*i+1
#define LS l,mid,lson
#define RS mid+1,r,rson
#define UP(i,x,y) for(i=x;i<=y;i++)
#define DOWN(i,x,y) for(i=x;i>=y;i--)
#define MEM(a,x) memset(a,x,sizeof(a))
#define gcd(a,b) __gcd(a,b)
#define LL long long
#define N 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define EXP 1e-8
#define lowbit(x) (x&-x)
#define MAX 6000
int a[MAX];
int n;
int tree[MAX];
void add(int x,int value) {
for (int i = x; i <= n; i+=lowbit(i)) {
tree[i] += value;
}
}
void init() {
MEM(tree,0);
}
int query(int x) {
int res = 0;
for (int i = x; i>0; i-=lowbit(i)) {
res += tree[i];
}
return res;
}
int main(int argc,char *argv[]) {
while(~scanf("%d", &n)) {
init();
int cnt = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
a[i];
add(a[i]+1,1);
cnt += i-query(a[i]+1);
}
int ans = cnt;
for (int i = 1; i <= n; i++) {
cnt += (n-a[i]-1)-a[i];
if (cnt < ans) ans = cnt;
}
printf("%d\n", ans);
}
return 0;
}