Number sequence
Source : SCU Programming Contest 2006 Final
Time limit : 1 sec Memory limit : 64 M
Given a number sequence which has N element(s), please calculate the number of different collocation for three number Ai, Aj, Ak, which satisfy that Ai < Aj > Ak and i < j < k.
Input
The first line is an integer N (N <= 50000). The second line contains N integer(s): A1, A2, …, An(0 <= Ai <= 32768).
Output
There is only one number, which is the the number of different collocation.
Sample Input 5
1 2 3 4 1
Sample Output 6
解题思路:按照题目的意思,求的是一个数组里面,每一个数的左边比它小的数的个数,乘以右边比它小的数的个数,然后把所以数求得的数据相加就是所得答案
当然这道题可以用暴力,就是把每个数的左边比它小的数的个数,和右边比它小的数的个数相乘,然后累加,思路很简单,但是肯定会超时,用树状数组的话就会巧妙很多…反正我觉得超级神奇…发明树状数组的人..真的是超级厉害的呀…
主要的想法就是用每个元素作为树状数组对应的下标,比如data[1]=5,则data[1]对应的树状数组的下标就是5,( c[5] );
然后还有一个很重要的就是一边输入一边录入,也就是说,是有顺序之分的,从i=1,开始录入的时候,有一个数的左边的数总是比它先录入,如果从i=n,开始录入,就有一个数右边的数总是比它先录入,这样就保证了求当前输入的数的左边比它小的数的个数的时候,不会出现它右边的数(它右边比它小的数)….求右边比它小的数也同理;树状数组的下标恰好又是从小到大的,因此data[i]左边的比它小的数的个数( 用lefts[i]记录每个date[i]的左边比它小的数的个数),就是query( data[i]-1 ),即树状数组的前data[i]-1项和;右边同理….从i=n开始录入即可,当然要把tree[i]清空
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=50010;
int tree[maxn];
int lefts[maxn];
int data[maxn];
int lowbit(int x)
{
return x&(-x);
}
void update(int pos)
{
while(pos<=maxn)
{
tree[pos]++;
pos+=lowbit(pos);
}
}
int query(int k)
{
int sum=0;
while(k>0)
{
sum+=tree[k];
k-=lowbit(k);
}
return sum;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++)///一边输入数据,一边把data[i]作为树状数组的下标
{
scanf("%d",&data[i]);
lefts[i]=query(data[i]-1);///lefts[i]记录第i个数的左边比它小的数的个数,
///因为data[i]是按照顺序先后录入的,所以当前的树状数组只存在当前录入的数,和比它早录入的数,即它左边的数
///因此求data[i]左边比它小的数的和就应该是query(data[i]-1)
update(data[i]);///这个数已经录入了,所以在树状数组这个数代表的下标处加上1
}
memset(tree,0,sizeof(tree));
long long res=0;
for(int i=n;i>0;i--)///从最右边的数开始录入
///query(data[i]-1)求的是这个数右边比它小的数
{
res+=lefts[i]*query(data[i]-1);
update(data[i]);
}
printf("%lld\n",res);
}
}