求排列的逆序数(线段树解法)

【题目】来源:xmuoj

【知识点】 线段树

线段树线段树主要思想是基于分治的思想,将一个大区间分成两个子区间,直到每个小区间只有一个元素,这样可以利用大区间的信息来解决小区间的问题,然后将小区间的结果逐级向上传递,得到整个区间的答案。

 【题目分析】

这道题是分治思想的经典运用,最经典的解法是归并。但这里我将选择线段树解法进行进一步的解释。

求排列的逆序数,一个数前面有k 个比他大的数那么这个数的逆序数就是k ,排列的逆序数就是所有数的逆序数之和。由此,问题转化为求第i 个数前面有多少个数比他大。

重新来观察数组,很容易发现,数组中最大的数a[i] ,前面没有任何数比它大;次大的数a[j] ,前面比它大的数可能有a[i] ,也可能没有,取决于i 与j 的大小关系;第三大的数也是如此。借此,我们提出一个新的思路:用ans 记录逆序数答案。从大到小遍历每个数(遍历时同时对已经遍历的数打个标记),对于每个数, ans 加上该数前方已经打过标记的数的个数(即比它大的数)即可。这样,我们只需要解决一个问题:一个数a[i] ,1~i 有多少个标记?熟悉数据结构即可容易发现,使用线段树或类似的数据结构,可以O(nlogn) 维护区间和,也就是维护1~i 标记个数。到这里,整套利用线段树的算法已经分析完毕。

    【核心代码】

  1. build(1,1,n);//建立线段树   
  2. sort(node+1,node+n+1,cmp);//排序   
  3. long long ans=0;  
  4. for(int i=n;i;i){  
  5.     ans+=(asksec(1,1,node[i].pos));//ans加上前方的标记和   
  6.     addsec(1,node[i].pos,node[i].pos,1);//打上标记   
  7. }  
  8. printf("%lld",ans);  
  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值