poj_2352 线段树
题目大意
对于二维平面上的n个点,给出点的坐标。定义一个点A覆盖的点的个数为满足以下条件的点B的个数:点B的x <= 点A的x坐标,点B的y坐标 <= 点A的y坐标。
给出N个点的坐标,求出覆盖点的个数分别为0, 1, ... N-1 的点各有多少个。
题目分析
对于二维平面的点问题,可以考虑先进行行列排序,然后进行处理。对点进行排序(y从小到大,y相同,x从小到大)之后,按照y从小到大进行:单独考虑一行的点的x坐标,此时x坐标是升序的,因此当前点的肯定可以覆盖当前行中的之前访问的点;对于下方的点,它们的y坐标肯定小于当前点的y坐标,因此只考虑点的x坐标,如果x坐标小于等于当前点的x坐标,则点被当前点覆盖。
于是问题就化为了,按照从左下到右上的顺序遍历每个点的时候,比较该点和之前访问过的点的x坐标,如果统计之前点中x坐标小于等于当前点x坐标的个数。也就相当于在x轴上从坐标0到坐标 point.x 这个区间内的点的个数,即一个区间统计问题。
区间统计问题,可以采用线段树来进行解决。具体做法是,线段树中的每个节点包含的区间为x坐标轴上的一个范围,遍历到一个点的时候,将点的x坐标插入到线段树中,线段树中的每个节点保存该节点所包含区间内被插入的点的个数。
这样可以通过两种方式来更新计数:
1. 从根向下插入点的时候,从根到叶子节点沿途经过的每个点的计数值w都加1
2. 更新到叶子节点的时候,叶子节点的w值加1,然后通过pushup操作,更新到父节点
实现(c++)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<algorithm> #define MIN(a, b) a <b? a:b #define MAX(a, b) a >b? a :b #define MAX_NUM 32005 #define MAX_NODE 15005 struct Point{ int x; int y; }; Point gPoints[MAX_NODE]; int gCoverNum[MAX_NODE]; //覆盖i个点的点的数目用 gCoverNum[i]表示 struct TreeNode{ int beg; int end; int w; //表示该节点所代表区间中被插入的点的个数,初始为0,之后每次插入时候,从上往下依次增加 int Mid(){ return (beg + end) / 2; } TreeNode(){ beg = end = w = 0; } }; TreeNode gTreeNodes[MAX_NUM * 4]; //初始化建树,主要初始化节点的边界和w void BuildTree( int node, int left, int right){ gTreeNodes[node].beg = left; gTreeNodes[node].end = right; gTreeNodes[node].w = 0; if (left == right){ return ; } int mid = (left + right) / 2; BuildTree(2*node + 1, left, mid); BuildTree(2*node + 2, mid + 1, right); } void PushUp( int node){ gTreeNodes[node].w = (gTreeNodes[2 * node + 1].w + gTreeNodes[node * 2 + 2].w); } //向以node为根的树中插入x,沿途中经过的节点的w值均加1 void Insert( int node, int x){ // gTreeNodes[node].w++; if (gTreeNodes[node].beg == gTreeNodes[node].end){ //不在上面 gTreeNodes[node].w++;,则在这里执行,这样在最后执行 pushup操作。其效果和 开始的时候执行gTreeNodes[node].w++;一样 gTreeNodes[node].w++; return ; } int mid = gTreeNodes[node].Mid(); if (x > mid){ Insert(2 * node + 2, x); } else Insert(2 * node + 1, x); //如果不使用上面的 gTreeNodes[node].w++;,则可以使用 PushUp操作,从下往上更新(由于递归的性质,会使得从叶节点到根都会被更新) //也就相当于 从上往下插入的时候,每经过一个点都将 w 值加 1 PushUp(node); } //在以node节点为根的树中查询区间 [s, e]中的元素数目 int Query( int node, int s, int e){ if (gTreeNodes[node].beg > e || gTreeNodes[node].end < s){ return 0; } if (gTreeNodes[node].beg >= s && gTreeNodes[node].end <= e){ return gTreeNodes[node].w; } int mid = gTreeNodes[node].Mid(); int sum = 0; sum += Query(2 * node + 1, s, MIN(mid, e)); sum += Query(2 * node + 2, MAX(s, mid + 1), e); return sum; } int main(){ int n; scanf( "%d" , &n); int max_end = 0; for ( int i = 0; i < n; i++){ scanf( "%d%d" , &gPoints[i].x, &gPoints[i].y); max_end = MAX(max_end, gPoints[i].x); gCoverNum[i] = 0; } BuildTree(0, 0, max_end); for ( int i = 0; i < n; i++){ int count = Query(0, 0, gPoints[i].x); gCoverNum[count] ++; Insert(0, gPoints[i].x); } for ( int i = 0; i < n; i++){ printf( "%d\n" , gCoverNum[i]); } return 0; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步