Time Limit: 10 second

Memory Limit: 2 MB

问题描述

给定一个序列a1,a2...an。如果存在i小于j 并且ai大于aj,那么我们称之为逆序对,求给定序列中逆序对的数目

Input

第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。

Output

所有逆序对的总数

Sample Input

4
3
2
3
2

Sample Output

3

【题解】

n的最大值为10W。

如果用线段树来解的话,求解的思路都相同。

我就直接复制前一篇的例子了。

比如

3 2 8 5

将它们排序(从大到小)

8 5 3 2

先把8放进去(原来的位置是3)

但是在放之前,先检查位置3之前有没有其他数字放进去了(如果放进去了肯定是比8大的数字,但是它们的下标又小于3(逆序对!));

因为没有

所以就把下标3对应的位置改为1;

即0 0 1 0

然后是第大的5(原来的位置是4)

则看看4前面有多少个元素已经放进去了(前缀和!)。

发现有1个。则答案递增1;

然后把arr[4] 改为1

即0 0 1 1

然后是第三大的数字3,它原来的位置是1,但是1前面没有数字已经放进去。则不递增答案。

最后是最小的元素2,它原来的位置就是2,然后位置2之前有一个数字3已经放在了位置1.即下标1的前缀和为1.则答案递增1.

最后答案为2;

这里实际上就是涉及到单节点的递增问题。

只要在改动的时候往上更新节点就可以了。很简单的线段树模型。

然后前缀和的话就是返回(1,pos)的和,线段树记录的是区间和的。

但是要注意一个问题。就是出现相同数字的情况。

则我们在写比较函数的时候,让相同的数字,之前的位置大的放在后面。然后我们处理到连续的相同数字的时候。就记录这是连续相同数字里面的第i个。

在累加完前缀和之后答案减去i-1,因为相同大小就不算逆序对了;

【代码】

#include <cstdio>
#include <algorithm> struct data2
{
int d, pos;
}; __int64 sum[400001] = { 0 },ans = 0; //线段树的数组貌似不用开4倍。。感觉2.2倍就够了。。
int n;
data2 a[100001];//以结构体的形式写,比较好写比较函数。 __int64 query(int l, int r, int begin, int end, int rt)//询问区间(l,r),当前节点的区间为(begin,end),当前节点为rt.
{
if (l > r)//如果询问的区间不合规则就退出
return 0;
if (l <= begin && end <= r) //如果这个节点所代表的区间在所要求的区间内,则返回这个区间的和。
{
return sum[rt];
}
int m = (begin + end) / 2;//获取这个节点所代表的区间的中点
__int64 re = 0;
if (l <= m)//如果左半部分有一部分区间在这个节点的左半部分则加上那一部分(当然会一直缩小区间直到找到那块区间为止)
re += query(l, r, begin, m, rt <<1);
if (m < r) //如果这个节点的区间的右半部分有一部分和所求区间有交集,则加上右半部分那段有交集的区间的和。
re += query(l, r, m+1, end, rt<<1|1);
return re;
} int cmp(const data2 &a, const data2 &b)//比较函数。
{
if (a.d > b.d)//第一关键是数字从大到小排序。
return 1;
if (a.d == b.d && a.pos < b.pos)//然后是如果数字相同则之前的位置大的在后面。
return 1;
return 0;
} void updata(int p, int num, int begin, int end, int rt)//要让数组下标为p的节点递增num,然后当前的节点所代表的区间为[begin,end],当前节点为rt
{//这里的p可以换成是区间理解为[p,p]
if (begin == end)//如果找到了这个节点p
{
sum[rt] += num;//在这个节点的和累加num
return;
}
int m = (begin + end) / 2;//取得这个节点所代表的区间中点
if (p <= m)//如果这个下标在中点的左边就往左递归节点
updata(p, num, begin, m, rt<<1);
else//否则往右递归节点
updata(p, num, m+1, end, rt<<1|1);
sum[rt] = sum[rt<<1] + sum[rt<<1|1];//因为子节点可能发生了改变,所以要更新当前节点的区间和。
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
//freopen("F:\\rush_out.txt", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i].d);
a[i].pos = i; //记录原先的位置
}
std::sort(a + 1, a + 1 + n, cmp);//进行从大到小排序
int now = 0;
for (int i = 1; i <= n; i++)//假设当前是连续相同的数字中第x个,则now始终等于x-1
{
if (i != 1 && a[i - 1].d == a[i].d)
now++;
else
now = 0;
ans += query(1, a[i].pos - 1, 1, n, 1);
ans -= now;//因为会重复累加相同的数字,所以要减掉那几个相同的。
updata(a[i].pos, 1, 1, n, 1);//把a[i].pos递增1,然后要修改与之相关的区间的和。
}
printf("%I64d", ans);
return 0;
}

【a703】求逆序对(线段树的解法)的更多相关文章

  1. HDU 6318.Swaps and Inversions-求逆序对-线段树 or 归并排序 or 离散化+树状数组 (2018 Multi-University Training Contest 2 1010)

    6318.Swaps and Inversions 这个题就是找逆序对,然后逆序对数*min(x,y)就可以了. 官方题解:注意到逆序对=交换相邻需要交换的次数,那么输出 逆序对个数 即可. 求逆序对 ...

  2. 逆序对 线段树&树状数组 (重制版)

    逆序对的定义:长度为n的数组a,求满足i<j时a[i]>a[j]条件的数对个数. 第一次接触这种问题的人可能是更先想到的是n^2去暴力数前面有几个比他大的数. int main() { i ...

  3. Day2:T4求逆序对(树状数组+归并排序)

    T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*( ...

  4. 2019.01.22 bzoj3333: 排队计划(逆序对+线段树)

    传送门 题意简述:给出一个序列,支持把ppp~nnn中所有小于等于apa_pap​的'扯出来排序之后再放回去,要求动态维护全局逆序对. 思路:我们令fif_ifi​表示第iii个位置之后比它大的数的个 ...

  5. hdu 4911 求逆序对数+树状数组

    http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能 ...

  6. loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

    $ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...

  7. 【bzoj3295】[Cqoi2011]动态逆序对 线段树套SBT

    题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序 ...

  8. POJ 2299 Ultra-QuickSort 求逆序数 线段树或树状数组 离散化

    我用的线段树写的. num数组表示已插入的数值的个数. 由于a[i]数值很大,但是n不是很大,所以要离散化处理 9 1 0 5 4 离散化后 4 1 0 3 2 这样保证最大值不会超过n #inclu ...

  9. 【a703】求逆序对(树状数组的解法)

    Time Limit: 10 second Memory Limit: 2 MB 问题描述 给定一个序列a1,a2...an.如果存在i小于j 并且ai大于aj,那么我们称之为逆序对,求给定序列中逆序 ...

随机推荐

  1. 洛谷 P1308 统计单词数

    P1308 统计单词数 题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定 ...

  2. Spider_basic

    网络爬虫 定义:网络蜘蛛.网络机器人,抓取网络数据的程序 总结:用Python程序去模仿人去访问网站,模仿的越逼真越好 目的:通过有效的大量数据分析市场走势.公司决策 企业获取数据的方式 公司自有数据 ...

  3. win10系统64位安装git后右键运行git bash here生成一个mintty.exe.stackdump文件后闪退解决方案

    在其他win10电脑上复制了一个null.sys文件,替换C:\Windows\System32\drivers\null.sys,搞定.

  4. Scala具体解释---------数组、元组、映射

    一.数组 1.定长数组 声明数组的两种形式: 声明指定长度的数组 val 数组名= new Array[类型](数组长度) 提供数组初始值的数组,无需newkeyword Scala声明数组时.须要带 ...

  5. 启动Tomcat,startup.bat一闪而过的解决办法

    1.打开命令行:win+R --> cmd2.将解压后的tomcat\bin\startup.bat文件拖到控制台窗口中,回车. 这样就可以看到错误信息的提示,根据提示修改即可.

  6. FindBug:Call to static DateFormat

    今天在重构代码的过程中碰到了一个问题.使用SimpleDateFormat的问题. 本人今天写了一个类,主要是提供很多静态的方法由其他接口调用,过程中多个方法使用到了日期的格式化,所以我讲Simple ...

  7. java学习顺序

    作者:阿弎 链接:http://www.zhihu.com/question/19851109/answer/91201815 来源:知乎 著作权归作者所有,转载请联系作者获得授权. ======== ...

  8. UVA 11489 - Integer Game 博弈

    看题传送门 题目大意: S和T在玩游戏,S先.给出一数字串,两人轮流取出一个数字,要求每次取完之后剩下的数为3的倍数,或者没有数字留下.如果两个人足够聪明,求胜利的一方. 思路: 我一开始竟然没有输C ...

  9. (转)Nginx在RedHat中系统服务配置脚本

    转自:http://binyan17.iteye.com/blog/1688308 以下代码是在前人的基础上,结合自己服务器实际情况修改的,本人服务器环境是:CentOS 6.31.创建启动脚本,  ...

  10. 【习题5-3 UVA-10935】Throwing cards away I

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 用STL的queue写 [代码] #include <bits/stdc++.h> using namespace st ...