http://acm.hdu.edu.cn/showproblem.php?pid=1394
部分來自http://blog.csdn.net/libin56842/article/details/8531117
寫給那些 剛入門線段樹,開始和我一樣對解題迷茫的人.
題意 求最小逆序數
逆序數的概念
如:0 3 4 1 2
設逆序數初始n = 0;
由於0後面沒有比它小的,n = 0
3後面有1,2 n = 2
4後面有1,2,n = 2+2 = 4;
所以該序列逆序數爲 4
其根據題意移動產生的序列有
3 4 1 2 0 逆序數:8
4 1 2 0 3 逆序數:6
1 2 0 3 4 逆序數:2
2 0 3 4 1 逆序數:4
所以最小逆序數爲2
思路:
首先題目規定 輸入的n個數是0到n-1的,不會大於等於n。假設一個序列 a[n],,逆序總數是 m , 第一個數是x, 那麼x的後面有 x 個數比 x 小,因爲包含0, 則第一個數移動最後一個,則 x 的 前面 有 n - x - 1個比 x 大, 所以這時候 逆序總數就是 m - x + n - x - 1;以此類推。 所以,我們只用知道初始序列的逆序數是多少即可, 當然方法很多,這裏講一下線段樹。如何建樹,每輸入一個數a[i],就查詢 比這個數大的區間[a[i]+1, n]內,有多少個數 前面 已經輸入過了, 就是存在,那麼用線段樹便可以解決了。
代碼
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
#define manx 5005
struct Tree
{
int left;
int right;
int num; //該區間內,已經出現的個數
}tree[4 * manx];
int a[manx];
void BuildTree(int i, int l, int r);
void Update(int i, int id);
int Query(int i, int l, int r);
int main()
{
int n;
while(~scanf("%d", &n))
{
int ans = 0;
BuildTree(1, 1, n);
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
Update(1, a[i] + 1);
ans += Query(1, a[i] + 2, n);//詢問比他大區間已經出現的次數
//printf("ans = %d\n", ans);
}
int m = ans;
for(int i = 0; i < n; i++)
{
m = m + n - 2 * a[i] - 1;
if(m < ans)
ans = m;
}
printf("%d\n", ans);
}
return 0;
}
void BuildTree(int i, int l, int r)//建樹
{
tree[i].left = l;
tree[i].right = r;
tree[i].num = 0;//初始化全部爲0,因爲還沒有輸入
if(tree[i].left == tree[i].right) return;
int mid = (l + r) >> 1;
BuildTree(i << 1, l, mid);
BuildTree(i << 1 | 1, mid + 1, r);
}
void Update(int i, int id)//更新
{
if(tree[i].left == id && tree[i].right == id)
{
tree[i].num = 1;//輸入過了,標記爲1
return;
}
int mid = (tree[i].left + tree[i].right) >> 1;
if(id > mid) Update(i << 1 | 1, id);
else Update(i << 1, id);
tree[i].num = tree[i << 1].num + tree[i << 1 | 1].num;//更新此區間內,已經出現過的總數
}
int Query(int i, int l, int r)//詢問該區間已經出現的次數
{
//printf("%d %d %d\n", i, l, r);
if(l > r) return 0;
if(tree[i].left == l && tree[i].right == r) return tree[i].num;
int mid = (tree[i].left + tree[i].right) >> 1;
if(r <= mid) return Query(i << 1, l, r);
else if(l > mid) return Query(i << 1 | 1, l, r);
else return Query(i << 1, l, mid) + Query(i << 1 | 1, mid + 1, r);
} //這裏是mid+1