题意:给定一个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;
}