UVALive-4329 Ping pong (树状数组)

题目大意:有n个数排成一列,问从中能找出几个三元组(ai,aj,ak)满足i<j<k并且这三个数严格单调。

题目分析:枚举中间的数字aj,如果aj前面有c(j)个数a(j)小,后面有d(j)个数比a(j)小,那么aj为中间数时,共有c(j)*(n-j-d(j))+d(j)*(j-1-c(j))。定义x(i)表示 i 出现过几次,当枚举到aj 时,只需统计sum(x(1)~x(a(j)-1)便得c(j),d(j)求法类似。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<vector>
# include<queue>
# include<list>
# include<set>
# include<map>
# include<string>
# include<cmath>
# include<cstdlib>
# include<algorithm>
using namespace std;
# define LL long long

const int N=1005;
const int INF=1000000000;
const LL oo=0x7fffffffffffffff;
const double eps=1e-10;

int n;
int maxn;
int a[N*100];
int b[N*100];
int c[N*100];
int d[N*100];

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

int Query(int x)
{
	int res=0;
	while(x>0)
	{
		res+=b[x];
		x-=lowbit(x);
	}
	return res;
}

void Update(int x,int k)
{
	while(x<=maxn)
	{
		b[x]+=k;
		x+=lowbit(x);
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		maxn=0;
		for(int i=1;i<=n;++i){
			scanf("%d",a+i);
			maxn=max(maxn,a[i]);
		}
		memset(b,0,sizeof(b));
		for(int i=1;i<=n;++i){
			c[i]=Query(a[i]-1);
			Update(a[i],1);
		}
		memset(b,0,sizeof(b));
		for(int i=n;i>=1;--i){
			d[i]=Query(a[i]-1);
			Update(a[i],1);
		}
		LL ans=0;
		for(int i=1;i<=n;++i){
			ans+=c[i]*(n-i-d[i]);
			ans+=d[i]*(i-1-c[i]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

  

 

posted @ 2016-04-25 21:35  20143605  阅读(384)  评论(0编辑  收藏  举报