洛谷 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
發佈了36 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章