HDU5091->线段树维护区间覆盖次数&&扫描线

博客介绍了如何使用线段树来维护区间覆盖次数,并结合扫描线解决HDU5091问题。通过将点视为长度为h的线段,利用扫描线方法将每个点拆分为两个点,分别表示入点和出点,从而计算最多能包含多少个点。

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

HDU5091->线段树维护区间覆盖次数&&扫描线

题意:

一个平面上有一些点,给出这些点的坐标,求用一个宽为w高为h的格子最多能包含到多少个点

题解:

用一根平行于y轴的扫描线维护沿x轴方向的宽度,而沿y轴方向的点的个数的计算则可以等价为求一个区间内线段覆盖的最多次数。
由于已经给出高度h,所以每个点都可以看成是以这个点的y坐标为起始,长度为h的一条线段。
这里扫描线的运用,就是把一个点拆分成两个点,一个点的x坐标为其原有的x值,这个点为入点,记其flag为1,另一个点的x坐标为x+w,这个点为出点,记其flag为-1


代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX 20005
struct Node
{
    int x , y1 , y2 , flag ;
}node[MAX];
struct Tree
{
    int l , r , Ma , lazy ;
}tree[MAX << 2];
int y[MAX] ;
bool comp(const struct Node a , const struct Node b)
{
    if(a.x == b.x) return a.flag > b.flag ;
    return a.x < b.x ;
}
void build(int p , int l , int r)
{
    tree[p].l = y[l] ;
    tree[p].r = y[r] ;
    tree[p].Ma = 0 ;
    tree[p].lazy = 0 ;
    if(l == r) return ;
    int mid = (l + r) >> 1 ;
    build(p << 1 , l , mid) ;
    build(p << 1 | 1 , mid + 1 , r) ;
}
void Pushdown(int p)
{
    if(tree[p].lazy != 0)
    {
        tree[p << 1].lazy += tree[p].lazy ;
        tree[p << 1].Ma += tree[p].lazy ;
        tree[p << 1 | 1].lazy += tree[p].lazy ;
        tree[p << 1 | 1].Ma += tree[p].lazy ;
        tree[p].lazy = 0 ;
    }
}
void update(int p , int l , int r , int y1 , int y2 , int flag)
{
    if(y1 <= tree[p].l &&y2 >= tree[p].r)
    {
        tree[p].Ma += flag ;
        tree[p].lazy += flag ;
        return ;
    }
    Pushdown(p) ;
    int mid = (l + r) >> 1 ;
    if(y2 <= y[mid]) update(p << 1 , l , mid , y1 , y2 , flag) ;
    else if(y1 > y[mid]) update(p << 1 |1 , mid + 1 , r , y1 , y2 , flag) ;
         else {
            update(p <<1 , l , mid , y1 , y[mid] , flag) ;
            update(p << 1 | 1 , mid + 1 , r , y[mid+ 1] , y2 , flag) ;
         }
    tree[p].Ma = max(tree[p<< 1].Ma , tree[p << 1 | 1].Ma) ;
}
int main()
{
    int n , w , h ;
    while(~scanf("%d" , &n) , n>0)
    {
        scanf("%d%d" , &w , &h) ;
        for(int i = 0 ; i < n ; i ++)
        {
            int x0 , y0 ;
            scanf("%d%d" , &x0 , &y0) ;
            node[i].x = x0 ;
            node[i].y1 = y0 ;
            node[i].y2 = y0 + h ;
            node[i].flag = 1 ;
            y[i+1] = y0 ;
            node[n+i].x = x0 +w ;
            node[n+i].y1 = y0 ;
            node[n+i].y2 = y0 + h ;
            node[n+i].flag = -1 ;
            y[n+i+1] = y0 + h ;
        }
        sort(node , node + 2*n , comp) ;
        sort(y+1 , y + 1 + n*2) ;
        int cnt = unique(y+1 , y + 2*n + 1) - y - 1 ;
        build(1 , 1 , cnt) ;
        int ans = 0 ;
        for(int i = 0 ; i < 2*n ; i ++)
        {
            update(1 , 1 , cnt , node[i].y1 , node[i].y2 , node[i].flag) ;
            ans = max(ans , tree[1].Ma) ;
        }
        printf("%d\n" , ans) ;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值