求逆序数 三种方法 归并排序 树状数组 线段树
交换次数即为逆序对数
poj1804数据范围小,int不会溢出,spoj上提价需用long long(注册spoj时,获取验证码时会访问谷歌,所以需要……)
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
const int maxn = 2e5 + 7;
int a[maxn], b[maxn];
long long cnt;
//函数里有个全局变零cnt保存交换次数,即1逆序数。对ary[l:r]排序,函数结束后,ary = des
void merge_sort(int ary[], int l, int r, int des[]) { //对ary[l..r]进行归并排序
if (l >= r) return;
int mid = (l + r) >> 1;
merge_sort(ary, l, mid, des);
merge_sort(ary, mid + 1, r, des);
int i = l, j = mid + 1, cur = l; //下面是合并
while (i <= mid && j <= r) {
if (ary[i] <= ary[j]) {
des[cur++] = ary[i++];
}
else { //逆序啦,ary[i] > ary[j], ary[j]要调到前(左)面
des[cur++] = ary[j++];
cnt += mid - i + 1; //关键看这里
//当ary[i] > ary[j]时,在前半部分中比ary[i]大的数都比ary[j]大,将ary[j]放在ary[i]前面的话,逆序数要加上mid + 1 - i
}
}
while (i <= mid) {
des[cur++] = ary[i++];
}
while (j <= r) {
des[cur++] = ary[j++];
}
memcpy(ary + l, des + l, sizeof(int) * (r - l + 1));
}
int main()
{
int T;
scanf("%d", &T);
for (int k = 1; k <= T; ++k) {
int n;
scanf("%d", &n);
cnt = 0;
for (int i = 0; i < n; ++i)
scanf("%d", a + i);
merge_sort(a, 0, n - 1, b);
//printf("%d", b[0]);
//for (int i = 1; i < n; ++i)
// printf(" %d", b[i]);
//puts(""); //检验归并排序是否正确
//printf("Scenario #%d:\n%d\n\n", k, cnt); //poj1804提交姿势,poj上数据范围小,int不会溢出
printf("%lld\n", cnt); //http://www.spoj.com/problems/INVCNT/en/
}
return 0;
}
BIT解法(适用于POJ1804)
#include <cstdio>
#include <cstring>
#define ADD 1000001
#define M 2000001
#define N 1007
using namespace std;
int ary[N], bit[M + 1], n;
int lowbit(int x) {
return x & (-x);
}
void add(int pos, int val) {
while(pos <= M) { //pos <= ?
bit[pos] += val;
pos += lowbit(pos);
}
}
int sum(int pos) { //询问1:pos的和
int res = 0;
while(pos > 0) {
res += bit[pos];
pos -= lowbit(pos);
}
return res;
}
int main() {
int T, kase = 0;
scanf("%d", &T);
while (T-- > 0) {
memset(bit, 0, sizeof bit);
scanf("%d", &n);
long long ans = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", ary + i);
ary[i] += ADD;
ans += (i - sum(ary[i])); //这两句难理解
add(ary[i], 1);
}
printf("Scenario #%d:\n%lld\n\n", ++kase, ans);
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5007;
int segTree[N << 1], ary[N];
void build(int rt, int l, int r) {
segTree[rt] = 0;
if (l == r) return;
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int pos) {
if (l == r) {
segTree[rt]++; //因为是求逆序数,所以加1
return;
}
int mid = l + r >> 1;
if (pos <= mid)
update(rt << 1, l, mid, pos);
else update(rt << 1 | 1, mid + 1, r, pos);
segTree[rt] = segTree[rt << 1] + segTree[rt << 1 | 1]; //别忘了更新父节点
}
int query(int rt, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return segTree[rt];
}
int mid = l + r >> 1, res = 0;
if (L <= mid)
res += query(rt << 1, l, mid, L, R);
if (R > mid)
res += query(rt << 1 | 1, mid + 1, r, L, R);
return res;
}
int main()
{
int n;
while (~scanf("%d", &n)) {
memset(segTree, 0, sizeof segTree);
int cnt = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", ary + i);
cnt += query(1, 0, n - 1, ary[i], n - 1);
update(1, 0, n - 1, ary[i]); //动态更新
}
int minCnt = cnt;
for (int i = 0; i < n; ++i) {
cnt += n - ary[i] - ary[i] - 1; //这一步操作可以求得队首元素放到队尾的逆序数,前提是数组ary[i]是0:n-1的一个排列
minCnt = min(minCnt, cnt);
}
printf("%d\n", minCnt);
}
return 0;
}