树状数组求逆序对原理

归并排序和树状数组都可以用nlogn的算法做到求出逆序对.但这里着重讲树状数组的原理与求法.
树状数组最常用的方面就是用来求逆序对, 普通方法需要n^2的复杂度, 而树状数组只需要用nlogn的复杂度, 所以是很好的优化, 关键在于内部函数lowbit的应用.
这是树状数组的结构图 : lowbit函数就是进行哪些实现之间的转化的, 因为这些数之间在二进制中存在着某种联系, 而lowbit函数便可把这些联系体现出来.!!!

image.png

总之记住树状数组实现的是nlogn的算法, 和他具体实现的是什么功能就行了.
当数据的范围较小时,比如maxn=100000,那么我们可以开一个数组c[maxn],来记录前面数据的出现情况,初始化为0;当数据a出现时,就令c[a]=1。这样的话,欲求某个数a的逆序数,只需要算出在当前状态下c[a+1,maxn]中有多少个1,因为这些位置的数在a之前出现且比a大。但是若每添加一个数据a时,就得从a+1到 maxn搜一遍,复杂度太高了。树状数组却能很好的解决这个问题,可以把数一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 i - getsum( c[i] ),其中 i 为当前已经插入的数的个数, getsum( c[i] )为比 c[i] 小的数的个数,i- getsum( c[i] ) 即比c[i] 大的个数, 即逆序的个数。最后需要把所有逆序数求和,就是在插入的过程中边插入边求和.

举个例子:有5个数,分别为5 3 4 2 1,当读入数据a=5时,c为:0,0,0,0,1;d为:0,0,0,0,1;当读入数据a=3时,c为:0,0,1,0,1;d为:0,0 , 1,1,1;当读入数据a=4时,c为:0,0,1,1,1;d为:0,0,1,2,1;
此思想的关键在于,读入数据的最大值为maxn,由于maxn较小,所以可以用数组来记录状态。当maxn较大时,直接开数组显然是不行了,这是的解决办法就是离散化。所谓离散化,就是将连续问题的解用一组离散要素来表征而近似求解的方法,这个定义太抽象了,还是举个例子吧。
   假如现在有一些数:1234 98756 123456 99999 56782,由于1234是第一小的数,所以num[1]=1;依此,有num[5]=2,num[2]=3,num[4]=4,num[3]=5;这样转化后并不影响原来数据的相对大小关系,何乐而不为呢!!!
还有一点值得注意,当有数据0出现时,由于0&0=0,无法更新,此时我们可以采取加一个数的方法将所有的数据都变成大于0的.

具体看代码 : 树状数组+去重离散化

#include<cstdio>
#include<algorithm>
#include<cstring>
#define CLR(x) memset(x,0,sizeof(x))
#define ll long long int
#define PI acos(-1.0)
#define db double
#define mod 1000000007
using namespace std;
const int maxn=1e5+5;
const db eps=1e-6;
const int inf=1e9;
const ll INF=1e15;
int c[maxn];         //树状数组
int lisan[maxn];   //用来离散的存离散后的结果数组
int s[maxn];        //用来存原始状态
int n,k;
//树状数组
int lowbit(int x)  //返回值最大1e5.
{
    return x&(-x);
}

void update(int i,int ans)
{
    while(i <= n){
        c[i] += ans;
        i += lowbit(i);
    }
}

int sum(int i)
{
    int s = 0;
    while(i > 0){
        s += c[i];
        i -= lowbit(i);
    }
    return s;
}
//结束
int main()
{
    while(~scanf("%d%d",&n,&k)){
        CLR(c);
        for(int i=0;i<n;i++){
            scanf("%d",&s[i]);
            lisan[i] = s[i];
        }
        sort(lisan,lisan+n);
        int len=unique(lisan,lisan+n)-lisan;
        ll res = 0;
        for(int i=0;i<n;i++){
            ll l=lower_bound(lisan,lisan+len,s[i])-lisan+1;
            update(l,1);
            res += i+1 - sum(l);
        }
        if(res < k) printf("0\n");
        else
            printf("%lld\n",res-k);
    }
}

说一说离散那部分.

/* 首先看题就知道肯定是需要进行离散化的, 还有一个问题就是可能有重数, 对于sort这种不稳定排序, 不考虑
*重数, 是对结果又影响的, 所以需要进行去重离散化, 一般的离散化也可以做到, 就是知道判到不重了, 就离散
* 化, 但是稍微有点麻烦, 所以这里就用到了, unique 和 lower_bound 函数, unique是使该数组中相同的部分去
* 掉, 而lower_bound是返回第一个大于或等于x的位置, 返回的都是地址.
*/
for(int i=0;i<n;i++){
            scanf("%d",&s[i]);
            lisan[i] = s[i];
}
sort(lisan,lisan+n);       //首先排序
int len=unique(lisan,lisan+n)-lisan;   //求出离散数组的长度并去重.
ll res = 0;
for(int i=0;i<n;i++){
int l=lower_bound(lisan,lisan+len,s[i])-lisan+1;    //实际上是返回的s[i] 在lisan数组中下标位置(从1开始) 然后
      //给原先那个数组所有相同的数都统一赋成一个值. 这样就可以到达去重的目的. l 既是离散化后的结果
update(l,1);
res += i+1 - sum(l);
}

所以实际上不管离不离散化都可以用这种方法.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容