您的位置:首页 > 其它

HDU 1394 解题报告 线段树 单点更新

2015-07-16 19:37 387 查看
求逆序对常用方法是归并排序,当然线段树也可以解决,但是空间复杂度就会更大。

这题两者都可用。因为只需要第一次求出逆序对数即可,后面的排列都可以推出来。

具体怎么推?

假设 sum 是第一次求出来的逆序对。 第一次排列是 1 3 6 9 0 8 5 7 4 2

那么,第二次就会是

3 6 9 0 8 5 7 4 2 1

1 被放到末尾,自然需要在 sum 上减去包括1的逆序对了。观察一下,1 是在最左边的,在1还在首部时,右边能跟1组成逆序对的就只有0,所以 sum 需要先减掉 1.

第三次 6 9 0 8 5 7 4 2

1 3 。同上, sum 要减掉 2.

回头看 第二次 ,1 被放在尾部后,可能会有新的逆序对产生,所以能跟尾部1组成逆序对的,没有, 所以是 sum + 0。

但是在第三次时候,前面总共有 0, 1, 2 三个可以跟尾部3组成逆序对。 所以这里就需要 sum + 3。

后面同样规律。

代码:

#include <cstdio>

#define MAXN (5000 + 10)
#define lson rt << 1
#define rson rt << 1 | 1

int ns[MAXN << 2];
int nums[MAXN];
int n, input, sum, ans;

void build(int rt, int l, int r)
{
if (l == r) ns[rt] = 0;
else {
int mid = (l + r) / 2;
build(lson, l, mid);
build(rson, mid+1, r);
ns[rt] = ns[lson] + ns[rson];
}
}

int query(int rt, int l, int r, int ll, int rr)
{

if (l == ll && rr == r) return ns[rt];
else {
int mid = (l + r) / 2;
if (rr <= mid) return query(lson, l, mid, ll, rr);
else if (mid < ll) return query(rson, mid+1, r, ll, rr);
else {
return query(lson, l, mid, ll, mid) + query(rson, mid+1, r, mid+1, rr);
}
}
}

void update(int rt, int l, int r, int id)
{
if (l == id && r == id) ns[rt] = 1;
else {
int mid = (l + r) / 2;
if (id <= mid)  update(lson, l, mid, id);
else update(rson, mid+1, r, id);
ns[rt] = ns[lson] + ns[rson];
}
}

int main()
{
//freopen("testdata/1394.txt", "r", stdin);
while (scanf("%d", &n) != EOF) {
build(1, 0, n);
sum = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &nums[i]);
sum += query(1, 0, n, nums[i]+1, n);
update(1, 0, n, nums[i]);
}
ans = sum;
for (int i = 0; i < n-1; ++i) {
//sum -= query(1, 0, n, 0, nums[i]-1); //这种方式同样是先减后加
//sum += query(1, 0, n, nums[i]+1, n); //不过需要注意nums[i]-1会等于-1.这时通过对nums[i]+1进行处理,而 n 就需要+1.
sum = sum - nums[i] + (n - nums[i] - 1);
if (ans > sum) ans = sum;
}

printf("%d\n", ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: