poj 2352

本文通过一道编程题目的实例解析了树状数组的应用方法。详细介绍了如何利用树状数组进行高效的数据处理,尤其是在处理坐标系中物体数量统计的问题上。文章深入浅出地解释了树状数组的工作原理和关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目概述:

有N个物

每个物坐标x,y,坐标系以左下角为原点

每个物有一个参数lvl,其值等于坐标系中位于其左下方(含正左,正下,但不含其自身)物的个数

输入:

第一行N,其后N行x,y,数据只有一组,物会按纵坐标递增呈现,纵坐标相同时按横坐标递增呈现

限制:

0<=N<=15000;0<=x,y<=32000

输出:

每行一个整数,若当前为输出的第m行(m从0开始),则输出lvl为m的物的个数

样例输入:

5
1 1
5 1
7 1
3 3
5 5

样例输出:

1
2
1
1
0

讨论:

树状数组的第一次练习,在理解上遇到一些困难,手边的资料包括题解对该题数据结构的介绍都不太充分,不过理解后实现并没难度,实际上额还写了一个简洁的版本,不过那个并不适合做题解

至于树状数组具体是什么,这里不在赘述,理论部分网络上可轻松找到一大片

另外看讨论版该题可将数据离散化(简单说就是用一个很小的标号来表示x,以减小空间占用),不过对于这道水题,反而会使代码更不简洁

这道题由于输入的特殊性,纵坐标完全成了摆设,因为纵坐标按递增呈现,因此后呈现的纵坐标必然大于等于先呈现的,即只需要比较横坐标就能判断位置,折合到数据结构中,

1.lvl全写是level,沿用原题的称呼,这里tree是树状数组的原始容器,其值记录横坐标小于等于下标的物的个数,这也是树状数组的原始用法,lvl即是题目中要求的参数

2.这两行(额自己)在理解上并不是很容易,一步步看,横坐标小于等于x的物共有read(x)个,由于修改tree数组的操作在下一行,因此这时还没有将当前节点计算在内,因此read(x)值(从tree中计算得来)即是当前物的lvl值,由于最后输出的是同一lvl的物个数,这里便+1,下一行mod(x,1)将该物加到数组tree中,另外,由于输入的特殊性,只要是后呈现的物,之前所有横坐标小于等于这个物的,都会贡献到lvl中,也就是说,直接将当前的read(x)(横坐标小于等于x的物的个数)拿来便是lvl,因此直接+1便可解决问题,扩展一点,如果输入没有这个特殊性该怎么办?额考虑的是人为地将其排序,这比实现判断read(x)中有多少不能算或开到二维树状数组要容易的多,尽管可能会牺牲时间渐近复杂度(排序的复杂度很可能高于数组操作)

题解状态:

348K,125MS,C++,690B

#include<algorithm>//在C++模式下,包含algorithm头文件便包含诸多常用头文件
using namespace std;
const int inf = 0x6f6f6f6f;
const int MAXN = 32005;

int tree[MAXN], lvl[MAXN];//参见讨论1
inline int lb(int a)//lowbit,这个东西的作用在树状数组资料中不会没写,不再赘述
{
	return a&(-a);
}
inline int read(int a)//读出到下标从1(含)到a(含)的元素的值的和,其实叫sum更合适,但sum属常用词汇,同时资料里也常称之为read
{
	int sum = 0;
	while (a > 0) {
		sum += tree[a];
		a -= lb(a);
	}
	return sum;
}
inline void mod(int a, int mod)//modify,利用加减法修改一个节点的值,并将其父节点值一并改变,该题中由于mod值恒为1,可将其具体化,这里只是方便作模版
{
	while (a <= MAXN) {//试图将MAXN改成N(令N为全局变量)只会得到一个错误的结果
		tree[a] += mod;
		a += lb(a);
	}
}
inline void fun(int N)
{
	for (int p = 0; p < N; p++) {
		int x, y;
		scanf("%d%d", &x, &y);//input//从这里之后y就没用了
		++x;//由讨论版,数据中会有x=0的情况,而树状数组的0下标不使用,因此这里+1避开这一情况,不需要其他额外处理
		++lvl[read(x)];
		mod(x, 1);//参见讨论2
	}
	for (int p = 0; p < N; p++)
		printf("%d\n", lvl[p]);//output
}
int main(void)
{
	//freopen("vs_cin.txt", "r", stdin);
	//freopen("vs_cout.txt", "w", stdout);

	int N;
	scanf("%d", &N);//input
	fun(N);
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值