树状数组求逆序数及运用模板

   花了一夜和一上午时间终于搞清楚了。树状数组下标不能从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

相关文章
相关标签/搜索