题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4911

题目大意:最多可以交换K次,就最小逆序对数

解题思路

逆序数定理,当逆序对数大于0时,若ak<ak+1,那么交换后逆序对数+1,反之-1。

设原始序列最小逆序对数=cnt

那么,交换K次的最小逆序对数max(0,cnt-k)

在求原始序列最小逆序对数上,朴素暴力复杂度O(n^2)不可取

有以下两种O(nlogn)的方法:

①排序内计算:

主要是利用归并排序内的特性,即相邻两个归并序列逆序情况不改变,[5,4,2,1]到[4,5]、[1,2]

在排序纠正逆序之后,4和1,5和2的逆序情况没有改变。利用这个性质,只要在归并排序对两个子序列merge排序时,统计逆序对数即可。

即,边排序,边统计,假设left、right序列是递归传递过来的序列从0开始重新编号之后,初始偏移,i=j=0

当left[i]>right[j]出现逆序情况时,cnt+=(leftnum-i),即当前right[j]元素和left[i]及以后元素都构成逆序对。

归并后,递归继续merge更大的序列。统计复杂度=排序复杂度O(nlogn)

注意归并排序的写法,left尾和right尾要设为inf,这样后跑完的序列会直接和inf比较。

g#include "cstdio"
#include "algorithm"
#define LL long long
using namespace std;
int a[];
LL cnt=;
void merge(int l,int m,int r)
{
int lnum=m-l+,rnum=r-m;
int *LEFT=new int[lnum+],*RIGHT=new int[rnum+];
for(int i=;i<lnum;i++) LEFT[i]=a[l+i];
for(int i=;i<rnum;i++) RIGHT[i]=a[m++i];
LEFT[lnum]=RIGHT[rnum]=0x3fffffff;
int i=,j=;
for(int k=l;k<=r;k++)
{
if(LEFT[i]<=RIGHT[j])
{
a[k]=LEFT[i];
i++;
}
else
{
a[k]=RIGHT[j];
j++;
cnt+=(lnum-i);
}
}
}
void mergeSort(int l,int r)
{
if(l<r)
{
int m=(r-l)/+l;
mergeSort(l,m);
mergeSort(m+,r);
merge(l,m,r);
}
}
int main()
{
//freopen("in.txt","r",stdin);
int n,k;
while(scanf("%d%d",&n,&k)!=EOF)
{
cnt=;
for(int i=;i<n;i++) scanf("%d",&a[i]);
mergeSort(,n-);
printf("%I64d\n",max((LL),cnt-k));
}
}

②树状数组:

很奇葩的方法。首先使用记录原始位置pos的排序,然后对排序后的元素进行离散化处理。

如序列5,1,1,离散化成2,1,1,树状数组sum[i]记录的是离散化位置被激活的次数,即add(Hash[i],1)

如离散化位置1,2,初始值[0,0], 首先按照输入顺序add离散化位置。

输入5,sum情况[0,1],那么树状数组getsum统计的是,在到此数的顺序数组上,被激活的个数。

用原始位置i-getsum,结果是,不含这个数,之前被激活的个数,即统计逆序情况。

如此时就是1,,这里由于1-1=0,即在5之前没有逆序对。

输入1,sum情况[1,1],getsum=1,i-getsum=1,有一个逆序对。[5,1],原因是5在1之前激活了。

输入1,sum情况[2,1],getsum=2, i-getsum=1,有一个逆序对。这里要对重复的数做add,因为重复的数,i增加了,

getsum也要对应的增加,不然,会和前面重复数的算重了,比如3-1=2,,就是算重了。

#include "cstdio"
#include "algorithm"
#include "cstring"
#include "map"
using namespace std;
#define LL long long
int sum[],n,k,val,N;
LL cnt;
int lowbit(int x) {return x&(-x);}
struct Num
{
int val,pos;
Num() {}
Num(int val,int pos):val(val),pos(pos) {}
bool operator < (const Num &a) const {return val<a.val;}
}a[];
LL getsum(int x)
{
LL ret=;
while(x>)
{
ret+=sum[x];
x-=lowbit(x);
}
return ret;
}
void update(int x,int d)
{
while(x<=N)
{
sum[x]+=d;
x+=lowbit(x);
}
}
int main()
{
freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&k)!=EOF)
{
memset(sum,,sizeof(sum));
map<LL,LL> Hash;
cnt=;
for(int i=;i<n;i++)
{
scanf("%d",&val);
a[i]=Num(val,i);
}
sort(a,a+n);
int id=;
Hash[a[].pos]=id;
for(int i=;i<n;i++) //离散化
{
if(a[i].val==a[i-].val) Hash[a[i].pos]=id;
else Hash[a[i].pos]=++id;
}
N=id;
for(int i=;i<n;i++)
{
update(Hash[i],);
cnt+=(i+-getsum(Hash[i]));
}
printf("%I64d\n",max((LL),cnt-k));
}
}

HDU 4911 (树状数组+逆序数)的更多相关文章

  1. hdu2838Cow Sorting(树状数组+逆序数)

    题目链接:点击打开链接 题意描写叙述:给定一个长度为100000的数组,每一个元素范围在1~100000,且互不同样,交换当中的随意两个数须要花费的代价为两个数之和. 问怎样交换使数组有序.花费的代价 ...

  2. HDU5196--DZY Loves Inversions 树状数组 逆序数

    题意查询给定[L, R]区间内 逆序对数 ==k的子区间的个数. 我们只需要求出 子区间小于等于k的个数和小于等于k-1的个数,然后相减就得出答案了. 对于i(1≤i≤n),我们计算ri表示[i,ri ...

  3. HDU3465 树状数组逆序数

    Life is a Line Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)T ...

  4. hdu 4638 树状数组 区间内连续区间的个数(尽可能长)

    Group Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  5. hdu 4777 树状数组+合数分解

    Rabbit Kingdom Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  6. [树状数组+逆序对][NOIP2013]火柴排队

    火柴排队 题目描述 涵涵有两盒火柴,每盒装有n根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑ (ai-bi)2,i=1,2,3,. ...

  7. HDU 1394 树状数组+离散化求逆序数

    对于求逆序数问题,学会去利用树状数组进行转换求解方式,是很必要的. 一般来说我们求解逆序数,是在给定一串序列里,用循环的方式找到每一个数之前有多少个比它大的数,算法的时间复杂度为o(n2). 那么我们 ...

  8. hdu 5497 Inversion 树状数组 逆序对,单点修改

    Inversion Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5497 ...

  9. HDU 2689Sort it 树状数组 逆序对

    Sort it Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

随机推荐

  1. CLR via C#(06)- 构造器

    最近忙着看新还珠,好几天不学习了.玩物丧志啊,罪过罪过. 今天总结的是类构造器的知识,其实这方面的文章蛮多的,可还是觉得亲自写一下对自己的思考和认识会有提高. 对于构造器,大家应该都不陌生,它主要是用 ...

  2. Jquery.Datatables dom表格定位

    Datatables会添加一些控制元素在表格的周围,比如默认状态下改变每页显示条数(l)的空间在左上角,即使搜索框(f)在右上角,表格的信息(i)显示在左下角,分页控件(p)显示在右下角. 这些控件在 ...

  3. OS Boot Loader -- 启动器

    这篇文章先抛出来,现在还没有彻底研究明白,但可以做个个人的小结和整理: 记得刚开始搞Linux的时候,普遍采用的是grub,后来有了grub2,尤其是在ubuntu那种非常差劲的不稳定的更新频繁的系统 ...

  4. 数据结构之图 Part3 – 1 遍历

    DFS using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ...

  5. WPF线程(Step1)——Dispatcher

    使用WPF开发时经常会遇上自己建立的线程需要更新界面UI内容,从而导致的跨线程问题. 异常内容: 异常类型:System.InvalidOperationException 异常描述: "S ...

  6. Effective C++ 之 0 导读(Introduction)

    Effective C++ 导读 (Introduction) 术语(terminology) 声明式 (declaration) 是告诉编译器某个东西的名称和类型(type),但略去细节.以下都是声 ...

  7. Android LayoutInflater详解 (转)

    在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById().不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例 ...

  8. Android ImageCache图片缓存,使用简单,支持预取,支持多种缓存算法,支持不同网络类型,扩展性强

    本文主要介绍一个支持图片自动预取.支持多种缓存算法的图片缓存的使用及功能.图片较大需要SD卡保存情况推荐使用ImageSDCardCache. 与Android LruCache相比主要特性:(1). ...

  9. 64位ubuntu下重新编译hadoop2.2流水账

    hadoop官方网站中只提供了32位的hadoop-2.2.0.tar.gz,如果要在64位ubuntu下部署hadoop-2.2.0,就需要重新编译源码包,生成64位的部署包.建议以下操作使用roo ...

  10. Convert XML to Object using LINQ

    Class and Xml : Please see my another article. http://www.cnblogs.com/mingmingruyuedlut/p/3436803.ht ...