zoukankan      html  css  js  c++  java
  • HDU 4911 (树状数组+逆序数)

    题目链接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[100005];
    LL cnt=0;
    void merge(int l,int m,int r)
    {
        int lnum=m-l+1,rnum=r-m;
        int *LEFT=new int[lnum+1],*RIGHT=new int[rnum+1];
        for(int i=0;i<lnum;i++) LEFT[i]=a[l+i];
        for(int i=0;i<rnum;i++) RIGHT[i]=a[m+1+i];
        LEFT[lnum]=RIGHT[rnum]=0x3fffffff;
        int i=0,j=0;
        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)/2+l;
            mergeSort(l,m);
            mergeSort(m+1,r);
            merge(l,m,r);
        }
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        int n,k;
        while(scanf("%d%d",&n,&k)!=EOF)
        {
            cnt=0;
            for(int i=0;i<n;i++) scanf("%d",&a[i]);
            mergeSort(0,n-1);
            printf("%I64d
    ",max((LL)0,cnt-k));
        }
    }
    View Code

    ②树状数组:

    很奇葩的方法。首先使用记录原始位置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[100005],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[100005];
    LL getsum(int x)
    {
        LL ret=0;
        while(x>0)
        {
            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,0,sizeof(sum));
            map<LL,LL> Hash;
            cnt=0;
            for(int i=0;i<n;i++)
            {
                scanf("%d",&val);
                a[i]=Num(val,i);
            }
            sort(a,a+n);
            int id=1;
            Hash[a[0].pos]=id;
            for(int i=1;i<n;i++) //离散化
            {
                if(a[i].val==a[i-1].val) Hash[a[i].pos]=id;
                else Hash[a[i].pos]=++id;
            }
            N=id;
            for(int i=0;i<n;i++)
            {
                update(Hash[i],1); 
                cnt+=(i+1-getsum(Hash[i]));
            }
            printf("%I64d
    ",max((LL)0,cnt-k));
        }
    }
    View Code

     

     

  • 相关阅读:
    C++中析构函数为什么要是虚函数
    依赖注入(IOC)
    ParseInt()与NaN()
    仿windows关机效果
    类似Tab的效果
    飞来飞去的广告
    Sql Server 三种连接
    JS日期处理
    绚丽的注册效果
    JS图片自动切换
  • 原文地址:https://www.cnblogs.com/neopenx/p/4465348.html
Copyright © 2011-2022 走看看