UVa 1428 - Ping pong

题目:从一串数字中选出三个有序的三元组,问有多少种取法。

分析:数据结构,树状数组。利用树状数组求每个数字两侧大和小的数字的个数为线性。

            ①按照插入顺序求解data[i]左边比它小的数字记为Lsmall[i];

            ②则data[i]左边比他大的数字为i-1-Lsmall[i],记为Lbiger[i];

            ③所有元素插入后,求出data[i]的右侧比他小的元素为:比它小的数字总数-Lsmall[i],记为Rsmall[i];

            ④则data[i]右侧比他大的数字为N-1-Rsmall[i],记为Rbiger[i];

            计算所有的Lsmall[i]*Rbiger[i] + Lbiger[i]*Rsmall[i]。

说明:使用long long类型,防止溢出。

#include <cstring>
#include <cstdio>

int data[20002];
int Lsmall[20002];
int Lbiger[20002];
int C[100001];

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

int sum(int k) 
{
	int value = C[k];
	while (k > lb(k)) 
		value += C[(k-=lb(k))];
	return value;
}

void add(int k) 
{
	C[k] += 1;
	while (k+lb(k) <= 100000) 
		C[(k+=lb(k))] += 1;
}

int main()
{
	int T, N, Rsmall;
	while (~scanf("%d",&T)) 
	while (T --) {
		scanf("%d",&N);
		for (int i = 1; i <= N; ++ i)
			scanf("%d",&data[i]);
		
		memset(C, 0, sizeof(C));
		for (int i = 1; i <= N; ++ i) {
			Lsmall[i] = sum(data[i]-1);//记录左边小于data[i]的数字个数 
			Lbiger[i] = i-1-Lsmall[i];//记录左边大于data[i]的数字个数 
			add(data[i]);
		}
		long long ans = 0LL;
		for (int i = 1; i <= N; ++ i) {
			Rsmall = sum(data[i]-1)-Lsmall[i];
			ans += 0LL+Lbiger[i]*Rsmall+Lsmall[i]*(N-i-Rsmall);
		}
		
		printf("%lld\n",ans);
	}
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值