• 洛谷 P1908 逆序对


    P1908 逆序对

    法一:归并排序求逆序对(不好理解,记一下)

    (此处用的是从大到小排序,毕竟求的是序列中ai>aj且i<j的有序对)

    在二路归并的时候,设l<=i<=mid,mid+1<=j<=r,要归并的是a[l]到a[mid]还有a[mid+1]到a[r]。只考虑a[l]到a[r]间产生的逆序对。

    在某时刻,要将a[i]或a[j]放入a1[k]位置时,显然i<=k<=j,当a[i]<=a[j]时,不产生逆序对;而a[i]>a[j]时,a[j]放在a[i]之前,a[l]到a[mid]中比a[i]大的数都比a[j]大,这样的数有mid-i+1个,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。

    #include<cstdio>
    int a[40001];
    int a1[40001];
    int num,n;
    void merge(int start,int mid,int end)
    {
        int k=start,k1=start,k2=mid+1;
        while(k1<=mid&&k2<=end)
        {
            if(a[k1]<=a[k2])
                a1[k++]=a[k1++];
            else
            {
                a1[k++]=a[k2++];
                num+=mid+1-k1;
            }
        }
        while(k1<=mid)
            a1[k++]=a[k1++];
        while(k2<=end)
            a1[k++]=a[k2++];
        for(int i=start;i<=end;i++)
            a[i]=a1[i];
    }
    void merge_sort(int start,int end)
    {
        if(start<end)
        {
            int mid=(start+end)/2;
            merge_sort(start,mid);
            merge_sort(mid+1,end);
            merge(start,mid,end);
        }
    }
    
    int main()
    {
        int i;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        merge_sort(1,n);
        printf("%d",num);
        return 0;
    }

    法二:树状数组

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    struct Num
    {
    	int data;
    	int num;
    	friend bool operator<(Num a,Num b)
    	{
    		return a.data<b.data||(a.data==b.data&&a.num<b.num);
    	}
    }a[50000];
    int n;
    int co[60000];
    int shu[240000];
    int lowbit(int x)
    {
    	return x&-x;
    }
    void change(int pos,int num)
    {
        while (pos<=n)
    	{
            shu[pos]+=num;
            pos+=lowbit(pos);
        }
    }
    int sum(int end)
    {
        int sum1=0;
        while (end>0)
    	{
            sum1+=shu[end];
            end-=lowbit(end);
        }
        return sum1;
    }
    int main()
    {
    	int i;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i].data);
    		a[i].num=i;
    	}
    	sort(a+1,a+n+1);
    	int id=1;
    	co[a[1].num]=1;
    	for(i=2;i<=n;i++)
    	{
    		if(a[i].data==a[i-1].data)
    			co[a[i].num]=id;
    		else
    			co[a[i].num]=++id;
    	}//至此为止是离散化,就是使各个数字间差更小并且不改变顺序
    	//例如1,1,2,7,5,9,11可以离散化为1,1,2,4,3,5,6
    	int ans=0;
    	for(i=1;i<=n;i++)
    	{
    		change(co[i],1);
    		ans+=i-sum(co[i]);//sum(co[i])计算的是树状数组中下标为1~co[i]的值的和,而树状数组中下标为i的值指的是离散化后值为i的数的数量
    		//因此sum(co[i])计算出的是离散化后小于等于co[i]的数的数量,i-sum(co[i])指的就是离散化后大于co[i]的数的数量 
    	}
    	printf("%d",ans); 
    	return 0;
    }


    法三:其他做法

    这题不用树状数组的n^2算法是这样的
    首先我们把序列存进q数组里,再开一个数组c,然后倒着扫描一遍数组
    对于q[i],我们从1~i横扫一遍c数组
    如果c[j]为真,说明 j 这个值比 i 这个值先被扫到,即原序列q中, j 在 i 后面
    而且我们是从1~i扫的c数组 所以毋庸置疑的j的值比i小
    哈哈满足逆序对条件 于是ans+=c[j]
    完成之后c[i]++最后输出ans

    这题不用树状数组的n^2算法是这样的
    首先我们把序列存进q数组里,再开一个数组c,然后倒着扫描一遍数组
    对于q[i],我们从1~i横扫一遍c数组
    如果c[j]为真,说明 j 这个值比 i 这个值先被扫到,即原序列q中, j 在 i 后面
    而且我们是从1~i扫的c数组 所以毋庸置疑的j的值比i小
    哈哈满足逆序对条件 于是ans+=c[j]
    完成之后c[i]++最后输出ans
  • 相关阅读:
    17.10.13
    17.10.12
    17.10.11
    17.10.10
    17.10.05
    17.10.04
    17.10.03
    17.10.02
    17.10.01
    17.9.29
  • 原文地址:https://www.cnblogs.com/hehe54321/p/8470458.html
Copyright © 2020-2023  润新知