hdu4000树状数组

https://vjudge.net/problem/HDU-4000

给出n个数,求所有的i<j<k且a[i]<a[k]<a[j]的个数

题目也就是让最后那个数第二大。我们输入第i个数字a,那么我们用的树状数组存的就是sum(a-1),sum(a-1)表示在a这个位置,在前i-1个数中,有sum(a-1)个数比a小,那么我们就可以求出在后面的(i+1,n)的序列中,有R=(n-a)-(i-1-sum(a-1))个数比a要大,(n-a)表示有这么多个数比a大,(i-1-sum(a-1))表示前i-1个数里面有sum(a-1)个数比a大,那么R=(n-a)-(i-1-sum(a-1))就表示后面有多少个数比a要大。然后,我们思考一下,a当作三个数里面的最小值,然后让它们组合,是不是就是有C(R,2)即R*(R-1)/2,因为后面的序列都是固定的,所以并没有排序的概念,也就是说,原本序列1 5 4 ,那么我们没有必要去思考1 4 5的情况,因为它已经固定好了(一开始死都想不通....),所以后面的序列选的时候直接从R个选2个就可以了,然后C(R,2)表示所有的a+两个大的(也就是小中大,小大中都包含在里面了),那么我们要求的是小大中,所以对于每一个a来说,我们都需要减去小中大的情况,那么小中大=sum(a-1)*R,所以最后答案就是对于(1,n)所有数,求和SUM(C(R,2)-sum(a-1)*R)

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
int n;
ll c[100009];
ll lowbit(ll x)
{
    return x&(-x);
}
ll getsum(ll x)
{
    ll sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
void update(ll x,ll val)
{
    while(x<=n)
    {
        c[x]+=val;
        x+=lowbit(x);
    }
}
int main()
{
    int i,T;
    scanf("%d",&T);
    int Count=1;
    while(T--)
    {
        ll ans=0;
        scanf("%d",&n);
        memset(c,0,sizeof(c));
        for(i=1; i<=n; i++)
        {
            ll a,L,R;
            scanf("%lld",&a);
            update(a,1);
            L=getsum(a-1);
            R=(n-a)-(i-1-L);//前面n-a的含义是有多少个数比a大
            //Left表示有多少个数比a小,(i-1-Left)表示i-1个数里面有这么多个数比a大
            ans+=R*(R-1)/2-L*R;
            //printf("Left==%lld  Right==%lld ans+=%lld  sum+=%lld\n",Left,Right,Right*(Right-1)/2,Left*Right);
            //sum%=100000007;
        }
        printf("Case #%d: %lld\n",Count++,ans%100000007);
    }
}


猜你喜欢

转载自blog.csdn.net/keepcoral/article/details/80550168
今日推荐