HOJ 2275 Number sequence(树状数组)

题意:给定一个n个元素的数列,令Ai, Aj, Ak,使得 Ai < Aj > Ak 且 i < j < k这样的三个数为一组。求出一共有多少组。

 

分析:n最大可达50000,常规的暴力枚举的话,复杂度为O(n^2),1s的时限肯定会超时。


考虑树状数组:

树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,能快速地获取连续几个数的和。

令num[i]做c[]的下标,来检索树状数组

对于本题数列中每输入一个元素num[i],就在树状数组num[i]位置,即c[num[i]]加1,表示这里有一个数。

这样,sum(num[i]-1) ,即c[1]+c[2]+......+c[num[i]-1],

便可以求出在i之前,比num[i]小的数的个数了。

先自左向右检索一遍,结果保存在lefts[i]数组中。这是ai左边比它小的数的个数。

再自右向左检索一遍,sum(num[i]-1)*lefts[i],即为当ai为中间那个数时,满足题目要求的组合的个数。

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=50010;
const int maxnn=32770;
int C[maxnn];   //用num[i]做下标来检索树状数组。而num[i]的范围是<=32768
int lefts[maxn];
int num[maxn];


int lowbit(int x)
{
    return x&(-x);
}

void insert(int pos)       //注意C[0]不能使用。这个函数的作用是在C[pos]处加1,表示这里有数字。
{
    while(pos <= maxn)
    {
        C[pos] ++;
        pos += lowbit(pos);
    }
}

int sum(int x)
{
    int sum = 0;
    while(x > 0)
    {
        sum += C[x];
        x -= lowbit(x);
    }
    return sum;
}


int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(C,0,sizeof(C));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&num[i]);   //总的思想:用num[i]做下标来检索树状树组。
            //这里正向检索。即检索num[i]前有几个数比它小。
            lefts[i]=sum(num[i]-1);   //树状数组前num[i]-1个数之和,即num[i]之前有几个数比它小。
            insert(num[i]);   //将树状数组的num[i]处元素加1,表示这个位置有了一个数。
        }
        //再逆向检索一遍.即反向利用num[i]处理数组C[]
       long long res=0;
        memset(C,0,sizeof(C));
        for(int i=n;i>=1;i--)
        {
            res+=lefts[i]*sum(num[i]-1);
            insert(num[i]);
        }
        printf("%lld\n",res);


    }
    return 0;
}
 


posted on 2012-10-03 16:38  MicZ  阅读(146)  评论(0编辑  收藏  举报