zoukankan      html  css  js  c++  java
  • 求逆序对

    求逆序对的常用方法(树状数组,归并排序,线段树)

    1.树状数组

    首先对数组b[i]进行离散化处理,按价值从大到小排序得到位置数组a[i],排序后用树状数组维护,将a[i](数从大到小排序后的位置)依次加入树状数组,然后依次查询a[i]位置前面一位的数,答案相加即为逆序对个数。
    例:洛谷P1908 逆序对

    #pragma GCC optimize("Ofast")
    #pragma GCC target("avx,avx2,fma")
    #pragma GCC optimize ("unroll-loops")
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    #include<string>
    #include<queue>
    #include<map>
    #include<stack>
    #include<iostream>
    #define INF 0x3f3f3f3f
    #define lowbit(a) ((a)&-(a))
    typedef long long ll;
    typedef unsigned long long ull;
    using namespace std;
    int n,c[500005],a[500005],b[500005];
    void update(int x,int y,int n)
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    	{
    		c[i]=c[i]+y;
    	}
    }
    ll getsum(int x)
    {
    	ll ans=0;
    	for(int i=x;i;i-=lowbit(i))
    	{
    		ans+=c[i];
    	}
    	return ans;
    }
    int cmp(int s1,int s2)
    {
    	if(b[s1]==b[s2])
    	return s1>s2;
    	else
    	return b[s1]>b[s2];
    }
    int main()
    {
    	ll ans=0;
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&b[i]);
    		a[i]=i;
    	}
    	sort(a+1,a+1+n,cmp);
    	for(int i=1;i<=n;i++)
    	{
    		update(a[i],1,n);
    		ans+=getsum(a[i]-1);
    	}
    	cout<<ans<<endl;
    } 
    

    2.归并排序

    如归并过程中右边小于左边,则逆序对个数加mid-i+1;

    #pragma GCC optimize("Ofast")
    #pragma GCC target("avx,avx2,fma")
    #pragma GCC optimize ("unroll-loops")
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    #include<string>
    #include<queue>
    #include<map>
    #include<stack>
    #include<iostream>
    #define INF 0x3f3f3f3f
    #define lowbit(a) ((a)&-(a))
    typedef long long ll;
    typedef unsigned long long ull;
    using namespace std;
    int n,a[500010],c[500010];
    long long ans;
    void msort(int b,int e)//归并排序
    {
        if(b==e)  
    		return;
        int mid=(b+e)/2,i=b,j=mid+1,k=b;
        msort(b,mid),msort(mid+1,e);
        while(i<=mid&&j<=e)
        	if(a[i]<=a[j])
        		c[k++]=a[i++];
        	else
        		c[k++]=a[j++],ans+=mid-i+1;//统计答案
        while(i<=mid)
        	c[k++]=a[i++];
        while(j<=e)
        	c[k++]=a[j++];
        for(int l=b;l<=e;l++)
        	a[l]=c[l];
    } 
    
    int main()
    {
        scanf("%d",&n); 
        for(int i=1;i<=n;i++)
        	scanf("%d",&a[i]);
        msort(1,n);
        printf("%lld",ans);
        return 0;
    }
    

    3.线段树

    与树状数组原理类似,进行单点修改,查询区间和;

  • 相关阅读:
    从代码书写理解指针,很重要
    指针参数 引用参数
    [转载]c/c++ 操作sqlite
    [转载]C#.NET DataTable扩展 ToList/Get/OrderBy/Where/ToPage
    [转载]宏定义
    【转载】scanf,sscanf高级用法
    C开发机顶盒实战应用代码片段
    指针精华口诀,短小精悍
    C# linq泛型 字典集合 排序
    c开发机顶盒实战应用代码片段2
  • 原文地址:https://www.cnblogs.com/qingjielaojiu/p/13517586.html
Copyright © 2011-2022 走看看