树状数组之--LA4329 Ping pong

LA4329 Ping pong 


问题分析:

当第i个人当裁判的时候,假设a[1]到a[i-1]中有c[i]个比a[i]小,就有(i-1)-c[i]个比a[i]大;同样的,假设a[i+1]到a[n]中有d[i]个比a[i]小,就有(n-i)-d[i]比a[i]大。

所以,当第i个人当裁判时,有c[i]*(n-i-d[i])+(i-1-c[i])*d[i]中比赛。

问题转换为求c[i], d[i]。

计算c[i],从左到右扫描所有的a[i],令x[j]=1/0表示当前扫描过的a[i]中是否有a[i]=j,c[i]=x[1]+x[2]+...+x[a[i-1]],如此这般求解前缀和。可以在O(nlogr)时间内计算所有的c[i]。计算d[i]同理。

我们动态的修改数组中的一个值,求解其连续和,正好可以使用树状数组。:)

/*
 * 4329.c
 *
 *  Created on: 25 April, 2015
 *  Author: gaoyida
 *  Problem: Ping Pong, Beijing 2008, LA 4329
 *  Method: BIT
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 20000+5
#define Maxn 100000+5
#define LL long long

LL c[N], d[N], x[Maxn];
int a[N], lowbit[Maxn];

void init() {
	int i;
	lowbit[0] = 0;
	for (i = 1; i <= 100000; ++i)
		lowbit[i] = i & -i;//lowbit[x]即x的二进制表达式中最右边的1所对应的值
}

void addForBit(LL cnt[], int i, int r) {
	while(i <= r) {
		cnt[i] += 1;
		i += lowbit[i];
	}
}

void sum(LL cnt[], LL f[], int i, int r) {
	int pos = a[i];
	LL ret = 0;
	addForBit(cnt, pos, r);
	pos = pos - 1;
	while (pos > 0) {
		ret += cnt[pos];
		pos -= lowbit[pos];
	}
	f[i] = ret;
}

int main() {
	int T;
	scanf("%d", &T);
	init();
	while (T--) {
		int n, i, r = 0;
		scanf("%d", &n);
		for (i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			if (a[i] > r) {
				r = a[i];
			}
		}
		memset(x, 0, sizeof(x));
		memset(c, 0, sizeof(c));
		for (i = 1; i <= n; ++i) {
			sum(x, c, i, r);
		}
		memset(x, 0, sizeof(x));
		memset(d, 0, sizeof(d));
		for (i = 1; i <= n; ++i) {
			sum(x, d, n-i+1, r);
		}
		LL res = 0;
		for (i = 2; i < n; ++i) {
			res += c[i] * (n-i-d[i]) + (i-1-c[i])*d[i];
		}
		printf("%lld\n", res);
	}
	return 0;
}

树状数组(二叉索引树):


在二叉索引树中,每层结点的lowbit相同,lowbit越大,越靠近根。对于结点i,如果它是左子结点,父节点编号是i+lowbit(i);如果是右子节点,父节点编号是i-lowbit(i)

构造出辅助数组C

Ci = Ai-lowbit(i)+1 + Ai-lowbit(i)+2 + ... + Ai

涉及两个操作:

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

void add(int x, int v) {
	while (x <= n) {
		C[x] += v;
		x += lowbit(x);
	}
}

这两个操作的时间复杂度都为O(logn)

树状数组对点修改求连续和代码简洁,如果出现了段修改求连续和可能线段树更好。


【1】 内容参考:《算法竞赛入门经典训练指南》


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值