樹狀數組求逆序數及運用模板

   花了一夜和一上午時間終於搞清楚了。樹狀數組下標不能從0開始,這樣會死循環,樹狀數組能夠求逆序數,可是若是要求一個序列中前面的元素小於等於當前元素的個數和怎麼求呢?好比:5個數:1 3 1 5 2;用b[]數組表示i前面有多少個數小於等於a[i]。則:b[1]=0,b[2]=1,b[3]=1,b[4]=3,b[5]=2。ios

   求逆序數你們可能都會,但網上不少模板都沒有離散化,致使結果錯誤,真是誤人子弟啊。。咱們若是有了求逆序數的模板,要求小於等於當前元素的個數就很好求了。逆序數:即i前面有多少個數大於a[i],那麼剩下的不就都小於等於a[i]了嘛。因此關鍵在於怎麼求逆序數。來看看個人板子:c++

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
ll p,a[N],sum[N],s[N];
int n;
int lowbit(int i)
{
    return i&(-i);
}
void update(int i)
{
    while(i<=n)
    {
        sum[i]+=1;
        i+=lowbit(i);
    }
}
ll get_sum(ll i)
{
    ll ans=0;
    while(i)
    {
        ans+=sum[i];
        i-=lowbit(i);
    }
    return ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        memset(sum,0,sizeof(sum));
        memset(s,0,sizeof(s));
        ll ans=0,Ans=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            s[i]=a[i];
        }
        //離散化
        sort(s,s+n);
        int k=unique(s,s+n)-s;
        for(int i=1; i<=n; i++) a[i]=(lower_bound(s,s+k,a[i])-s)+1;//保證下標從1開始

        for(int i=1; i<=n; i++)
        {
            ll tmp=get_sum(a[i]);
            update(a[i]);
            ans+=tmp;//i前面小於等於a[i]的數個數
            Ans+=i-1-tmp;//i前面小於等於a[i]的數個數,逆序數
//        printf("i==%d a[i]==%lld tmp==%lld  ans==%lld\n",i,a[i],tmp,i-tmp);
        }
        printf("%lld\n",ans);//小於等於當前元素的個數總和
        printf("%lld\n",Ans);//求逆序數
    }
    return 0;
}

get_sum()與update()操做能夠反過來,但ans與Ans的減一操做就得互換了。數組

特別須要注意數據範圍。spa

若有不足之處,歡迎批評指正。code


來看一道例題:blog

                                        E - Meaningful Mean

 5月13號atcoder上的題,在一個月後居然成了咱們的校賽題。。遺憾的是寫了兩個小時最終沒有寫出來,這道題也能夠用值域線段樹寫,但樹狀數組可能稍微簡潔一點。

題意:給你n個數和k,求有多少個區間的平均數大於等於k。ip

思路:開始毫無頭無,無從下手,但仔細分析,每一個數減去k後不就成了求有多少個區間的和大於等於0,再進一步分析,求一個前綴和,就至關於求i前面小於等於當前前綴的個數,由於兩個前綴和相減既是這個區間的和。get

這個題的坑點在於一個數也算一個區間。若是簡單求每一個數前面有多少個數小於等於它那是不行的,由於第一個數前面沒有數,但單獨這個數就可能大於等於p。因此應該在第一個數前面再增長一個0,而後再離散化。string

#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include<functional>
using  namespace std;
typedef long long ll;
#define sc(x) scanf("%d",&x)
#define pd(x) printf("%d\n",x)
const int INF=1e9+10;
const int MOD=1e9+7;
const double eps=1e-5;
const double pi=acos(-1.0);
const int N=2e5+5;
ll p,a[N],sum[N],s[N];
int n;
int lowbit(int i)
{
    return i&(-i);
}
void update(int i)
{
    while(i<=n)
    {
        sum[i]+=1;
        i+=lowbit(i);
    }
}
ll get_sum(ll i)
{
    ll ans=0;
    while(i)
    {
        ans+=sum[i];
        i-=lowbit(i);
    }
    return ans;
}
int main()
{
    while(~scanf("%d%lld",&n,&p))
    {
        memset(sum,0,sizeof(sum));
        memset(s,0,sizeof(s));
        ll ans=0,Ans=0;
        a[0]=0;
        s[0]=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            a[i]-=p;
            a[i]+=a[i-1];
            s[i]=a[i];
        }
        n++;
        sort(s,s+n);
        int k=unique(s,s+n)-s;
        for(int i=n; i>=1; i--) a[i]=a[i-1];
        for(int i=1; i<=n; i++) a[i]=(lower_bound(s,s+k,a[i])-s)+1;
        for(int i=1; i<=n; i++)
        {
            update(a[i]);
            ll tmp=get_sum(a[i]);
            ans+=tmp-1;//i前面小於等於a[i]的數個數
            Ans=i-tmp;
//        printf("i==%d a[i]==%lld tmp==%lld  ans==%lld\n",i,a[i],tmp,i-tmp);
        }
        printf("%lld\n",ans);
//        printf("%lld\n",Ans);//求逆序數
    }
    return 0;
}

 千萬記得離散化。it

相關文章
相關標籤/搜索