線段樹求逆序數

線段樹是一類非常有用的數據結構 這個可以體現到求解一組序列的逆序數上來

可以這麼想 我們求逆序數的時候,對於每一個數字都找前面比他大的數字的數目

例如 9 5 7 9這三個序列 我們先找5前面比5大的數字的個數 很明顯是9 有一個

我們繼續找7前面比7大的個數 也是9 

找 9 前面比9大的個數 和明顯沒有 

按照這個想法實現的是O(n^2)的一個算法 複雜度不是很好 

既然是區間操作 我們想到了線段樹 

我們先建立一個空樹 然後呢 按照這個序列的順序將每個值插入 ,每次插入伴隨一次查詢 查詢前面插入的比這次插入的大的數字的個數

然後就可以了 複雜度 nlogn 可以接受

需要注意 :1.很多時候可能要離散化 這個很容易想 因爲如果數字是long long 類型以及以上的那麼空間就爆了

 2.注意建立空樹的時候要在 1-n+1上建立空樹 因爲有可能查詢n query(1,n+1,n)的時候就出錯了 這樣並不影響結果,仔細體會一下

下面這是沒有離散的代碼

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
int arr[maxn];
struct node{int sum,l,r;};
node segtree[4*maxn];
int fa[maxn];
void buildtree(int num,int l,int r)
{
    if(l==r)
    {
        segtree[num].l=segtree[num].r=l;
        fa[r]=num;
        segtree[num].sum=0;
        return ;
    }
    segtree[num].l=l;
    segtree[num].r=r;
    segtree[num].sum=0;
    int mid=(l+r)/2;
    buildtree(2*num,l,mid);
    buildtree(2*num+1,mid+1,r);
}
int query(int num,int l,int r)
{
    if(segtree[num].l==l&&segtree[num].r==r)
    {
        return segtree[num].sum;
    }
    int mid=(segtree[num].l+segtree[num].r)/2;
    if(l>mid)
    {
         return query(2*num+1,l,r);
    }
    else if(r<=mid)
    {
        return query(2*num,l,r);
    }
    else
    {
        return query(2*num,l,mid)+query(2*num+1,mid+1,r);
    }
}
void update(int num,int x)
{
    if(segtree[num].l==segtree[num].r)
    {
        segtree[num].sum++;
        return ;
    }
    int mid=(segtree[num].l+segtree[num].r)/2;
    if(x<=mid)
        update(2*num,x);
    else update(2*num+1,x);
    segtree[num].sum++;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>arr[i];
    buildtree(1,1,n+1);
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        sum+=query(1,arr[i]+1,n+1);
        update(1,arr[i]);
    }
    cout<<sum<<endl;
    return 0;
}





下面是離散化方式 :

#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;
int arr[1000];
int save[1000];
vector<int> v;
int getid(int x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>arr[i];
        v.push_back(arr[i]);
    }
    sort(v.begin(),v.end()),unique(v.begin(),v.end());
    for(int i=1;i<=n;i++)
        save[i]=getid(arr[i]);
    return 0;
}

注意離散化的時候一定要保證用了sort和unique 並且unique要在sort之後纔可以用

但是用了stl很容易t所以離散化儘量自己做 下面是進行離散化 線段樹求逆序操作

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <ctype.h>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define inf 1e9+7
int arr[1100200];
int brr[1100200];
int hashh[1100200];
int save[1100200];
int sum[3300200];
vector<int> q;
int n,m;
void update(int num,int l,int r,int x)
{
    if(l==r) {sum[num]++;return;}
    int mid=(l+r)>>1;
    if(x<=mid) update(num<<1,l,mid,x);
    else if(x>mid) update((num<<1)|1,mid+1,r,x);
    sum[num]++;
}
int query(int num,int l,int r,int left,int right)
{
    if(l==left&&r==right) return sum[num];
    int mid=(l+r)>>1;
    if(right<=mid) return query(num<<1,l,mid,left,right);
    else if(left>mid) return query((num<<1)|1,mid+1,r,left,right);
    else return query(num<<1,l,mid,left,mid)+query((num<<1)|1,mid+1,r,mid+1,right);
}
int getid(int x) {return lower_bound(save+1,save+m+1,x)-save;}
int main()
{
    while(scanf("%d",&n)!=EOF&&n){
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&arr[i]);
        hashh[i]=arr[i];
    }
        sort(hashh+1,hashh+n+1);
        m=1;save[1]=hashh[1];
        for(int i=2;i<=n;i++) if(hashh[i]!=hashh[i-1]) save[++m]=hashh[i];
        int size=m;
        for(int i=1;i<=n;i++) brr[i]=getid(arr[i]);
    long long cnt=0;
    for(int i=1;i<=n;i++)
    {
        update(1,1,size+10,brr[i]);
        cnt+=(long long)query(1,1,size+10,brr[i]+1,size+10);
    }
    cout<<cnt<<endl;
    }
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章