zoukankan      html  css  js  c++  java
  • 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++)

    #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
    ", gCoverNum[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    HashMap源码分析jdk1.8
    Struts1.x总结
    session的使用
    浅谈EL
    浅谈JavaBean
    try、catch、finally带return的执行顺序总结
    jvm内存模型
    left join 、right join 、inner join之间的区别
    js按键事件
    log4j配置详解
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4717376.html
Copyright © 2011-2022 走看看