poj_2182 线段树/树状数组

题目大意

n个数排成一排(不知道大小,只是占了一个位置),从a[1]到a[n]进行遍历,对于每个a[i],给出从a[1]到a[i-1]中小于a[i]数的个数。要求出 a[1]到a[n]中这n个数的相对顺序。

题目分析

对于每个数 a[i], 给出了从 a[1] -- a[i-1]中小于a[i]的个数 less[i]. 
    从n到1逆序查看, less[n] 表示前n-1个数中小于a[n]的个数,则可以确定a[n]的位置为 less[n] + 1 
类似的对于 i,为了确定a[i]在所有n个数中的序号,将这个任务分为两部分: 
(1)在 a[1] -- a[i-1]中有多少个数小于a[i], 题目给出了为 less[i] 
(2)在a[i+1]---a[n]中有多少个数小于 a[i], 设为t

则 a[i] 在所有n个数中的序号(按照从小到大排序)为 k = less[i] + t + 1

但是,t并不好直接求出,则观察k的性质。对于a[i]在所有n个数中的位置k,1---k-1中包括 less[i]个在 a[1] -- a[i-1]中的元素,同时包括t个在a[i+1]---a[n]中的元素,在a[i+1]---a[n]中的元素已经确定了他们在整个n个数中的位置(我们是从后往前进行计算的),则 1----k-1中就可以确定那t个元素的位置。

为了确定k的位置,则设置一个数组b[1]--b[n],初始全部为0,从n到1统计,若a[i]的位置确定下来为p,则 b[p] = 1.则对于任意的k,b[1]---b[k]中1的个数表示 1----k中被占用的位置,0 的位置表示未被占用的位置。

对于 a[i],找到某个k,使得其b[1]--b[k-1]中0的个数正好为 less[i]个,则k的位置就是 a[i]在整个n个数中的按照大小排序的位置

实现(c++)

1. 线段树

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>

#define MAX_COW_NUM 80010
#define MAX_NODE_NUM 4*MAX_COW_NUM

int gLess[MAX_COW_NUM];
int gPos[MAX_COW_NUM];
struct Node{
	int beg;
	int end;
	int sum_zero;
	int Mid(){
		return (beg + end) >> 1;
	}
};
Node gNodes[MAX_NODE_NUM];
void BuildTree(int beg, int end, int index){
	gNodes[index].beg = beg;
	gNodes[index].end = end;
	if (beg == end){
		gNodes[index].sum_zero = 1;
		return;
	}
	int left = 2 * index + 1;
	int right = 2 * index + 2;
	int mid = (beg + end) >> 1;
	BuildTree(beg, mid, left);
	BuildTree(mid + 1, end, right);
	gNodes[index].sum_zero = gNodes[left].sum_zero + gNodes[right].sum_zero;
}
//对于每个数 a[i], 给出了从 a[1] -- a[i-1]中小于a[i]的个数 less[i].
//从n到1逆序查看, less[n] 表示前n-1个数中小于a[n]的个数,则可以确定a[n]的位置为 less[n] + 1
//类似的对于 i,为了确定a[i]在所有n个数中的序号,将这个任务分为两部分:
//(1)在 a[1] -- a[i-1]中有多少个数小于a[i], 题目给出了为 less[i]
//(2)在a[i+1]---a[n]中有多少个数小于 a[i], 设为t

//则 a[i] 在所有n个数中的序号(按照从小到大排序)为 k = less[i] + t + 1

//t 并不好直接求出,则观察k的性质。对于a[i]在所有n个数中的位置k,1---k-1中包括 less[i]个在 a[1] -- a[i-1]中的元素,
//同时包括 t个在a[i+1]---a[n]中的元素,在a[i+1]---a[n]中的元素已经确定了他们在整个n个数中的位置(我们是从后往前进行计算的),
//则 1----k-1中就可以确定那t个元素的位置。

//为了确定k的位置,则设置一个数组 b[1]--b[n],初始全部为0,从n到1统计,若a[i]的位置确定下来为p,则 b[p] = 1.
//则对于任意的k,b[1]---b[k]中1的个数表示 1----k中被占用的位置,0 的位置表示未被占用的位置。

//对于 a[i],找到某个k,使得其b[1]--b[k-1]中0的个数正好为 less[i]个,则k的位置就是 a[i]在整个n个数中的按照大小排序的位置

//利用线段树,找到 b[1]---b[pos]中 0的个数为k个的pos的位置
void FindKth(int k, int index, int& pos){
	if (gNodes[index].sum_zero < k){
		return;
	}
	if (gNodes[index].beg == gNodes[index].end){
		gNodes[index].sum_zero = 0;
		pos = gNodes[index].beg;
		return;
	}
	int left = 2 * index + 1, right = 2 * index + 2;
	gNodes[index].sum_zero--;
	if (gNodes[left].sum_zero >= k){
		FindKth(k, left, pos);
	}
	else{
		FindKth(k - gNodes[left].sum_zero, right, pos);
	}
}

int main(){
	int n;
	scanf("%d", &n);
	BuildTree(0, n - 1, 0);
	for (int i = 2; i <= n; i++){
		scanf("%d", &gLess[i]);
	}
	int pos = 0;
	gLess[1] = 0;
	for (int i = n; i >= 1; i--){
		FindKth(gLess[i] + 1, 0, pos);
		gPos[i] = pos + 1;
	}
	for (int i = 1; i <= n; i++){
		printf("%d\n", gPos[i]);
	}
	return 0;
}

 2. 树状数组

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define MAX_COW_NUM 80010
int gLowBit[MAX_COW_NUM];
int gC[MAX_COW_NUM];
int gLess[MAX_COW_NUM];
int gPos[MAX_COW_NUM];
bool gUsed[MAX_COW_NUM];
void InitLowBit(int n){
	for (int i = 1; i <= n; i++){
		gLowBit[i] = i&(-i);
	}
}
void InitSequence(int n){
	for (int i = 1; i <= n; i++){
		gC[i] = gLowBit[i];
	}
}

//树状数组的更新
void Update(int p, int n, int add){
	while (p <= n){
		gC[p] += add;
		p += gLowBit[p];
	}
}

//树状数组的查询
int Query(int p){
	int result = 0;
	while (p > 0){
		result += gC[p];
		p -= gLowBit[p];
	}
	return result;
}

//二分法,查找满足要求的 位置
int Search(int k, int n){
	int beg = 1, end = n + 1;
	while (beg < end){
		int mid = (beg + end) >> 1;
		int sum_zero = Query(mid);
		if (sum_zero == k){
			while (mid + 1 < end){		//用于判断该位置是否被占用
				if (gUsed[mid + 1])
					mid++;
				else
					break;
			}
			return mid + 1;
		}
		else if (sum_zero < k){
			beg = mid + 1;
		}
		else{
			end = mid;
		}
	}
	return 1;
}

int main(){
	int n;
	scanf("%d", &n);
	gLess[1] = 0;
	InitLowBit(n);
	InitSequence(n);
	memset(gUsed, false, sizeof(gUsed));

	for (int i = 2; i <= n; i++){
		scanf("%d", &gLess[i]);
	}
	for (int i = n; i >= 1; i--){
		int pos = Search(gLess[i], n);
		gPos[i] = pos;
		gUsed[pos] = true;
		Update(pos, n, -1);
	}
	for (int i = 1; i <= n; i++){
		printf("%d\n", gPos[i]);
	}
	return 0;
}
时间: 2024-10-29 05:16:33

poj_2182 线段树/树状数组的相关文章

线段树&amp;数状数组

线段树 单点修改,区间查询 #include<bits/stdc++.h> using namespace std; int n,q; long long num[1000010]; struct tree { int l,r; long long sum,max; }t[4000010]; void BuildTree(int,int,int); void Update(int,int,int,int,long long); long long Query(int,int,int,int,i

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

[luogu P3801] 红色的幻想乡 [线段树][树状数组]

题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操

【算法系列学习】线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵

https://vjudge.net/contest/66989#problem/A 单点修改,区间查询 方法一:线段树 http://www.cnblogs.com/kuangbin/archive/2011/08/15/2139834.html 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<cmath> 6 #

hdu1540 Tunnel Warfare 线段树/树状数组

During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly con

UVA 11990 ”Dynamic“ Inversion(线段树+树状数组)

[题目链接] UVA11990 [题目大意] 给出一个数列,每次删去一个数,求一个数删去之前整个数列的逆序对数. [题解] 一开始可以用树状数组统计出现的逆序对数量 对于每个删去的数,我们可以用线段树求出它在原序列中的逆序对贡献 在线段树的每个区间有序化数据,就可以二分查找出这个数在每个区间的位置, 这样就处理出了划分出的区间的贡献,先用答案减去这一部分 接下来考虑已经删去部分的容斥,我们发现只要对删去部分再做一次类似的操作, 将这一部分跟当前删去数求一次贡献就是刚才多减去的部分,将这部分的答案

士兵杀敌(四)(树状数组+线段树)

士兵杀敌(四) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧. 假设起始时所有人的军功都是0. 输入