题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目大意:依次将数组中第一个数放到数组尾,形成n组序列,求这n个序列中的最小逆序数
题目分析1:逆序其实就是指前面的数大于后面的数,每两个这样的数就称为一对,求逆序数就是求序列里有几对这样的数。这道题和上一道一样都是可以用线段树或者树状数组来做的。
代码参考1:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 50000+9;
int n;
int c[maxn];
char cmd[10];
int lowbit(int x)//找到目前节点的父节点
{
return x&(-x);//t的二进制,从后往前数的第一个1
}
void update(int x, int y)//x为数组下标位置,y为要增加的值
{
while(x <= n)
{
c[x] += y;//单点更新
//父节点也更新,这个过程实际上也只是一个把末尾1后补0的过程
x += lowbit(x);
}
}
int getsum(int x))//求解前缀和
{
int sum = 0;
while(x > 0)
{
sum += c[x];
//这一步实际上等价于将i的二进制表示的最后一个1剪去,再向前数当前1的权个数
x -= lowbit(x);
}
return sum;
}
int main()
{
int i;
while(~scanf("%d", &n))
{
memset(c, 0, sizeof(c));
memset(num, 0, sizeof(num));
int sum = 0;
for(i=1; i<=n; ++i)//注意树状数组的下标是从1开始的
{
scanf("%d", &num[i]);
//getsum计算的是1~某数的和,
//因此如果要计算a~b之间的区间和需要1~a的区间和减去1~b的区间和
sum += getsum(n)-getsum(num[i]+1);
update(num[i]+1);
}
int ans = sum;
for(i=1; i<=n-2; ++i)
{//我们假设abcde中小于a的个数为x[i],大于a的为n-x[i],a左移一位,原来比a大的现在都成了a的逆序对,减少x[i]+1,
sum += n-2*num[i]-1;//总共加了n-x[i]-x[i]-1;
ans = min(ans, sum);
}
printf("%d\n", ans);
}
return 0;
}
题目分析1:
……
代码参考1:
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int M = 5009;
int num[M];
struct SegNode {
int left, right, delay;
void init(int a, int b)
{
delay = 0;
left = a;
right = b;
}
} seg[M << 2];
int L(int n)
{
return n << 1;
}
int R(int n)
{
return n << 1 | 1;
}
void build(int N, int a, int b)
{
seg[N].init(a, b);
if(a == b) { return ; }
int mid = (a + b) >> 1;
build(L(N), a, mid);
build(R(N), mid + 1, b);
}
void update(int N, int id)
{
seg[N].delay++;
if(seg[N].left == id && seg[N].right == id) { return ; }
if(seg[L(N)].right >= id) { update(L(N), id); }
else { update(R(N), id); }
}
int query(int N, int a, int b)
{
if(seg[N].left >= a && seg[N].right <= b) {
return seg[N].delay;
}
int mid = (seg[N].left + seg[N].right) >> 1;
if(mid >= b) { return query(L(N), a, b); }
else if(a >= mid + 1) { return query(R(N), a, b); }
else {
int x = query(L(N), a, mid);
int y = query(R(N), mid + 1, b);
return x + y;
}
}
int main()
{
int n, sum, i;
while(~scanf("%d", &n)) {
build(1, 0, n - 1);
sum = 0;
for(i = 0; i < n; ++i) {
scanf("%d", &num[i]);
if(num[i] + 1 <= n - 1) { sum += query(1, num[i] + 1, n - 1); }
update(1, num[i]);
}
int ans = sum;
for(i = 0; i < n; ++i) {
sum += n - num[i] * 2 - 1;
ans = min(ans, sum);
}
printf("%d\n", ans);
}
return 0;
}