• 树状数组 --- (离散化+树状数组、求逆序对)



    http://blog.51cto.com/image/skin/30/tj.png

                   G.Inversions     

    There are N integers (1<=N<=65537) A1, A2,.. AN (0<=Ai<=10^9). You need to find amount of such pairs (i, j) that 1<=i<j<=N and A[i]>A[j].

    Input

    The first line of the input contains the number N. The second line contains N numbers A1...AN.

    Output

    Write amount of such pairs.

    Sample Input


    Sample test(s)

    Input
    5
    2 3 1 5 4

    Output
    3
     
    【题目来源】:BUN
    【题目大意】:求逆序对的个数
    【题目分析】
    求逆序对有很多方法,比如说用合并排序、分治、树状数组、线段树,甚至连暴力(冒泡排序)也可以做,但是要注意会不会超时。
    这里就讲一下树状数组的方法,这一题最有意思的是离散化的方法,这个方法在处理大数据的排序方面很有用,离散化能够有效的降低时空复杂度,他可以改进一个低效的算法。除了加上了一个离散化,其他的用树状数组就可以解决。
    1、什么是离散化?
    用我的理解来说就是一种映射,为什么能用离散化呢?或者说离散化能用在哪些方面呢?
    举个例子说吧 ,在排序、求逆序对这些和顺序有关的题目中就能用离散化。搜索帖子你会发现有各种说法,比如“排序后处理”、“对坐标的近似处理”等等。哪个是对的呢?哪个都对。关键在于,这需要一些例子和不少的讲解才能完全解释清楚。
    下面就模拟一个数列的离散化:
    首先定义一个结构体:
    struct Node
    {
        long long num;
        int index;
    };
    Node node[MAX];

    和一个数组a[MAX],然后输入五个数:8 1 6 7 4

    for(i=1;i<=n;++i)
    {
        scanf("%lld",&node[i].num);
        node[i].index=i;
    }

    node[i].num存储了数组的值,而node[i].index存储的是他的下标,也就是序号。

    按照num来进行排序:

    bool cmp(Node a,Node b)
    {
        return a.num<b.num;
    }
    stable_sort(node+1,node+n+1,cmp); /**< 这是一个重点,后面会单独讲一下。 */

    排好序以后再用:

    for(i=1;i<=n;i++)
    {
        a[node[i].index]=i;
    }

     这样就将原来的8 1 6 7 4转化为5 1 3 4 2,比较一下这个序列和原来的序列有什么区别?你会发现他记录了原来数组的大小和元素顺序,这两个就通过离散化很好的结合在一起了,而且将原来很大的数据压缩为一串从1开始的连续的数,大大降低了时空复杂度。

    然后问题就简单了,将a数组更新到树状数组当中去,进行统计就出答案了。

    开始的时候我一直都没弄清楚树状数组是怎么实现求逆序对的,后来也是看别人的博客,模拟了一下才搞懂的。

    原理是什么呢?

    在插入a[i]之前,我们先统计比a[i]小的数或等于a[i]的数有几个,也就是getsum(a[i]),然后再用i-getsum(a[i]),这样就得到了在他前面并且比他大的数据的个数。这样也很好理解,总数-小于或等于本身的个数=大于本身的个数,先更新再统计。

    还有这题需要用long long ,开始的时候没想到害我wrong了好多次,然后静下心来算了一算发现确实要用long long,在从下往上累加的时候,最上面的那个数最大时相当于65537的平方,65537的平方就是四十多亿,int最多二十亿,妥妥的超了。用long long就过了。还有一个细节,就是当输入的序列中有数字相同时要怎么处理?首先处理这个问题时你得透彻的知道离散化的过程。

    有两种解决方法:

    方法一:在离散化的过程中进行处理,也就是在离散化过程中遇到两个数相同时,你得将它标记为两个数,这样就避免了相同时只计算一个数这种情况。

    具体怎么来实现呢?很简单,看代码,不解释:

            for(i=1;i<=n;i++)
            {
                    scanf("%d",&a[i].y);
                    a[i].x=i;
            }
            //qsort(a+1,n,sizeof(a[1]),cmp);
            sort(a+1,a+n+1,cmp);
            b[1]=1;
            x1=1;
            for(i=2;i<=n;i++)       
            {
                    if(a[i].y==a[i-1].y)
                            b[a[i].x]=x1;
                   else
                            b[a[i].x]=++x1;
            }
            for(i=1;i<=n;i++)
            {
                    update(b[i],1);
                    num+=i-sum(b[i]);
            }

    方法二:直接用stable_sort,即:

     stable_sort(node+1,node+n+1,cmp);

    现在就来讲一下sort和stable_sort的区别:

    这两个函数的原理都是快速排序,时间复杂度在所有排序中最低,为O(nlog2n) ;但是stable_sort要稍微慢一点。

    sort的应用;

    1、可以传入两个参数;

         sort(a,a+N) ,其中a是数组,a+N表示对a[0]至a[N-1]的N个数进行排序(默认从小到大排序);

    2、传入三个参数;

         sort(a,a+N,cmp),第三个参数是一个函数 ;

         如果让函数从大到小排序,可以用如下算法实现;

          bool cmp(int a,int b){return a>b};

          sort(A,A+N,cmp);

    而stable_sort的用法与sort一致,区别是stable_sort函数遇到两个数相等时,不对其交换顺序;这个应用在数组里面不受影响,当函数参数传入的是结构体时,会发现两者之间的明显区别。

    这题如果在离散化的时候不进行处理,后面又用sort,肯定妥妥的跪了,我就是这样啊,血的教训。。。

    最好的解决办法就是不管什么情况下都用stable_sort,这样就不会出现这种问题了。

    下面贴一下代码:

     方法一:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    struct point
    {
    	int x,y;
    }a[65540];
    int b[65540],c[65540];
    int cmp(point a,point b)
    {
    	return a.y<b.y;
    }
    int lowbit(int x)
    {
    	return x&(-x);
    }
    void update(int x,int y)
    {
    	while(x<=65537)
    	{
    		c[x]+=y;
    		x+=lowbit(x);
    	}
    }
    int sum(int x)
    {
    	int sum=0;
    	while(x>0)
    	{
    		sum+=c[x];
    		x-=lowbit(x);
    	}
    	return sum;
    }
    int main()
    {
    	int n,i,x1;
    	__int64 num;
    	while(scanf("%d",&n)!=EOF)
    	{
    		num=0;
    		memset(a,0,sizeof(a));
    		memset(b,0,sizeof(b));
    		memset(c,0,sizeof(c));
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i].y);
    		a[i].x=i;
    	}
    	//qsort(a+1,n,sizeof(a[1]),cmp);
    	sort(a+1,a+n+1,cmp);
    	b[1]=1;
    	x1=1;
    	for(i=2;i<=n;i++)       
    	{
    		if(a[i].y==a[i-1].y)
    			b[a[i].x]=x1;
                   else
    			b[a[i].x]=++x1;
    	}
    	for(i=1;i<=n;i++)
    	{
    		update(b[i],1);
    		num+=i-sum(b[i]);
    	}
    	printf("%I64d
    ",num);
    	}
    	return 0;
    }

    方法二:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstdlib>
    #define MAX 70000
    using namespace std;
    long long tree[MAX];
    long long a[MAX];
    struct Node
    {
        long long num;
        int index;
    };
    Node node[MAX];
    bool cmp(Node a,Node b)
    {
        return a.num<b.num;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x)
    {
        while(x<MAX)
        {
            tree[x]++;
            x+=lowbit(x);
        }
    }
    int getsum(int x)
    {
        int sum=0;
        while(x>0)
        {
            sum+=tree[x];
            x-=lowbit(x);
        }
        return sum;
    }
    int main()
    {
        int n;
        while(scanf("%d",&n)!=EOF)//这题不能用while(scanf("%d",&n),n),为此wrong了n次
        {
            memset(tree,0,sizeof(tree));
            int i,j,k,l;
            for(i=1;i<=n;++i)
            {
                scanf("%lld",&node[i].num);
                node[i].index=i;
            }
            stable_sort(node+1,node+n+1,cmp);
    //        check
    //        for(i=1;i<=n;i++)
    //        {
    //            printf("%d ",node[i].num);
    //        }
            //对排序后的数组进行离散化
            for(i=1;i<=n;i++)
            {
                a[node[i].index]=i;
            }
    //        for(i=1;i<=n;i++)
    //        {
    //            printf("%d ",a[i]);
    //        }
            //入树+统计
            long long ans=0;
            for(i=1;i<=n;i++)
            {
                update(a[i]);
    //            cout<<getsum(a[i])<<endl;
                ans+=(i-getsum(a[i]));
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

      

      

     

     
  • 相关阅读:
    C#中使用My实现单例应用程序
    喝着啤酒学Python(2):第一个HelloWorld
    再读《精通css》04:盒模型和空白边叠加
    再读《精通css》07:圆角
    再读《精通css》08:阴影
    @ResponseBody 乱码
    再读《精通css》05:定位、浮动与清理
    关于javascript面向对象的一点思考
    再读《精通css》06:背景图片
    【求解释】关于第三方接口调用中安全的疑问
  • 原文地址:https://www.cnblogs.com/crazyacking/p/3681376.html
Copyright © 2020-2023  润新知