hdu 1394 Minimum Inversion Number 树状数组求逆序数对(原理)

好吧,这是作为ACM弱渣的我第一次写博客,值得纪念!!!在acm集训时接触树状数组不久,只能说对它神奇的位运算惊叹不已,但是实际运用到题目,只能惊叹好难运用,做不出来。那么对于hdu1394这题关于求逆序数对,对于大神自然是一道超级水题,但是我也是苦思冥想,为什么可以运用树状数组加速。也许很多新手像我一样,思维限制在了树状数组那些较为复杂的运算中(或许我逻辑能力较差,各位大神勿喷),朦朦胧胧地感觉到是这么算的,但是又没有办法用逻辑想通,在这里,小弟我觉得应该先把树状数组放下,不要考虑它。 


  假设树状数组为c[i],它所要加速的数组为a[i],输入的为b[i],我们先只考虑a[i]和b[i],朴素的想法是每输入一个b[i],就判断从0<=j<=i-1,如果b[j]>b[i],那么ans++(当然,这道题完全可以这么水过去)。那么现在,每输入一个b[i],我就用a[b[i]+1]++去标记总共输入多少次了(因为输入的数据是0~n-1,所以输入的b[i]就相当于a[i]的下标i,而且有0,树状数组无法对下标为0进行操作,所以要a[b[i]+1]),那么现在很明显了,对于一个b[i],要想查询它前面有多少大于它的,只需将a[b[i]+1]到a[n]加起来,也就是求一段数组的和,那么树状数组就上场加速了。



 现在,总结一下,我觉得作为新手刚刚接触树状数组无须太过于钻研它如何实现还有它的树状结构,我们应该先关注它的功能,那三个函数代码能实现的功能是什么,就像刚刚接触STL,无须关注它如何实现,会用并且用得正确即可,所以对于树状数组,我们做题时只需抽象出如果一个数组是需要求一段区间的和,并且数组的值会随时变化,那么求和就用树状数组,管它的结构呢,随着学习的深入,我相信会越来越了解它,所以无须一开始就钻牛角尖,每个阶段的学习都要要抓住重点。


第一次写博客,写的不好,请大家指导。


题目:hdu 1394

题意:给定一个数组,先求逆序数对,在通过移动,求最小的逆序数对。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int c[5005], n, a[5005];
int lowbit(int t)
{
	return t&(-t);
}
int getsum(int pos)
{
	int ans = 0;
	while (pos > 0)
	{
		ans += c[pos];
		pos -= lowbit(pos);
	}
	return ans;
}
void update(int pos, int val)
{
	while (pos <= n)
	{
		c[pos] += val;
		pos += lowbit(pos);
	}
}
int main(void)
{
	while (scanf("%d", &n) != EOF)
	{
		int ans = 0;
		memset(c, 0, sizeof(c));
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &a[i]);
			ans +=getsum(n)- getsum(a[i] + 1);//编号最大是n-1,加一后是n
			update(a[i] + 1, 1);
		}
		int cnt = ans;
		//printf("%d\n", ans);
		for (int i = 0; i < n; i++)
		{
			cnt = cnt + n - 1 - 2 * a[i];
			ans = min(ans, cnt);
		}
		printf("%d\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值