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

24 篇文章 0 订阅

求逆序对常用方法是归并排序,当然线段树也可以解决,但是空间复杂度就会更大。

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


具体怎么推?


假设 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);
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值