洛谷 P4378 —— 樹狀數組求逆序對

題意:
求冒泡排序算法的“趟數”
思路:
一趟冒泡排序可以可以還原一個數的一個逆序對,那麼我們需要求出一個數最多的逆序對個數就是冒泡的趟數。
拿樣例來舉個例子:

5
1
5
3
8
2
有逆序對(5,3(5,2)(3,2)(8,2)  其中逆序對最多的是 2
第一趟冒泡以後 變成 1 3 5 2 8 消除了(53)(82)
第二趟冒泡以後 變成 1 3 2 5 8 消除了(52)
第三趟冒泡以後 變成 1 2 3 5 8 消除了(32)

除了樸素求逆序對,常見的求逆序對主要有兩種方法。

  1. 歸併排序求逆序對
  2. 樹狀數組求逆序對

此題用歸併排序的方法無法確認最多的逆序對個數(也可能存在,但是我不會…)
所以採用樹狀數組求逆序對,順便記錄一下樹狀數組求逆序對的原理:
衆所周知,樹狀數組主要是用來維護前綴和,所以我們要把求逆序對的問題轉換成一個前綴和問題。
步驟:

  1. 先將數列排序
  2. 然後按原序列的順序,把數列中的數插入到樹狀數組中。在插入數的時候,將已經插入的數所在的位置 ii ,打一個標記值 設bk[i]=1bk[i] = 1每次拿已經插入的數字個數 tottot - bk[]bk[] 的前 ii項和 把其中的差累加就是 逆序對的個數。
    拿樣例來舉個例子:
    在這裏插入圖片描述
  • 第一次插入 11 ,在下標 11 下面打個標記 1,共插入tot=1tot = 1個數,前 11 個數的前綴和爲sum=1totsum=0ans=0sum = 1,tot - sum = 0,ans = 0

  • 第二次插入 55 ,在下標 44 下面打個標記 1,共插入tot=2tot = 2個數,前 44 個數的前綴和爲sum=2totsum=0ans=0sum = 2,tot - sum = 0,ans = 0

  • 第三次插入 33 ,在下標 33 下面打個標記 1,共插入tot=3tot = 3個數,前 33 個數的前綴和爲sum=2totsum=1ans+=1ans=1sum = 2,tot - sum = 1,ans += 1,ans = 1

  • 第四次插入 88 ,在下標 55 下面打個標記 1,共插入tot=4tot = 4個數,前 55 個數的前綴和爲sum=4totsum=0ans=0sum = 4,tot - sum = 0,ans = 0

  • 第五次插入 22 ,在下標 22 下面打個標記 1,共插入tot=5tot = 5個數,前 22 個數的前綴和爲sum=2totsum=3ans+=3,ans=4sum = 2,tot - sum = 3,ans += 3,ans = 4

共4個逆序對。
原理:
注:a[]a[]是排好序的數列。

因爲插入的順序是原數組的順序,但是標記按排好序的順序打的,這完全符合逆序對的定義:假設在原數組中 a[i]a[i] 排在 a[j]a[j] 前面,我們先插入a[i],ia[i],i 處打上標記,然後再插入a[j],ja[j],j 處打上標記, 此時如果i<ji<jiijj的前面 ,我們統計前 jj 個數的前綴和sumsum是可以統計到 ii 的,相反如果i>ji>j,ii 就在 jj 的後面,我們就統計不到,所以在 jj 前面插入 ,又大於a[j]a[j]的數就可以和a[j]a[j] 構成逆序對。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
 
const int N = 1e5+7;
int n,tree[N];
int ans;
void add(int k,int num)
{
    while(k<=n)
    {
        tree[k]+=num;
        k+=k&-k;
    }
}
 
int read(int k)
{
    int sum=0;
    while(k)
    {
        sum+=tree[k];
        k-=k&-k;
    }
    return sum;
}
struct node
{
    int val,pos;
}a[N];
bool cmp(node a,node b)
{
    return a.val < b.val;
}
int main(void)
{
	//freopen("data.in","r",stdin);
    int i,j;
    int b[N];
    while(scanf("%d",&n)==1)
    {
        memset(tree,0,sizeof(tree));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i].val);
            a[i].pos = i;
        }
        sort(a+1,a+1+n,cmp);
        int cnt = 1;
        for(i=1;i<=n;i++)
        {
            if(i != 1 && a[i].val != a[i-1].val)
                cnt++;
            b[a[i].pos] = cnt;
        }
        for(i=1;i<=n;i++)
        {
            add(b[i],1);
            ans = max(ans,i - read(b[i]));
        }
        printf("%lld\n",ans+1);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章