http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目链接,这题想了一段时间。关键是数据是0-n-1这个突破口,利用我们插入线段树的先后就可以很快求出来有多少个逆序对了。
例如:
4
2 1 3 4 这个案例,
我按顺序肯定先插入2的,这时候我线段树L=R=2这个点就置为1,然后往上面更新啊。于是个区间个数加一。
然后问题来了,我插入1,这时候按照逆序对的定义我先找比他大的,还要比他之前插入的(这里涉及到一个细节,代码给出)于是,很简单,查询比他大的数字的区间有多少个数字,于是我查询2-4区间,发现有一个1,于是我逆序数就+1;后面的也是这样操作。(题目数字是0-N-1)
然后这题目还有个地方就是往后面移动数字,这个根据手推,原来第一的数字被放到尾巴了,就需要考虑
1.应为是头,前面没数字,于是我们可以得出一个结论,放在尾巴后面之后,之前比他大的数字全部与他自己构成逆序对,又因为,数字区间是连续的,而且重0开始,所以,会增加n-1-NUM[i](num是放输入的数组)
2.之前比他小的,现在已经不是逆序数了,有多少个呢?也应为是头,而且连续,所以有NUM[I]个。。。我们减去他
于是,综合2点,就有
ans = ans + n - 2 * num[i] - 1;
#include<iostream>
#include<algorithm>
using namespace std;
int tree[100000];
int num[1000000];
void updata(int x,int l,int r,int k)
{
if (l == r)
{
tree[k] = 1;
return;
}
int mid = (l + r) >> 1;
if (x <= mid)
updata(x, l, mid, k << 1);
else
updata(x, mid + 1, r, k << 1|1);
tree[k] = tree[k << 1] + tree[k << 1 | 1];
}
int qury(int x, int y, int l, int r, int k)
{
if (l == x&&r == y)
{
return tree[k];
}
int mid = (l + r) >> 1;
if (y <= mid)
{
return qury(x, y, l, mid, k << 1);
}
else if (x > mid)
{
return qury(x, y, mid + 1, r, k << 1 | 1);
}
else
return qury(x, mid, l, mid, k <<1) + qury(mid + 1, y, mid + 1, r, k << 1 | 1);
}
int main()
{
int n;
while (cin >> n)
{
memset(num, 0, sizeof(num));
memset(tree, 0, sizeof(tree));
int ans = 0;
for (int i = 1; i <= n; i++)
{
cin >> num[i];
ans += qury(num[i], n - 1, 0, n - 1, 1);//细节,如果放下面一层,最后一个数字假如是最大的,导致会多出几个数,而这个数是最后的,所以是不算的
updata(num[i], 0, n - 1, 1);
}
int min = ans;
for (int i = 1; i <= n; i++)
{
ans = ans + n - 2 * num[i] - 1;
if (min > ans)
min = ans;
}
cout << min << endl;
}
}