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