油漆面积 线段树+线扫描 蓝桥杯 java

X星球的一批考古机器人正在一片废墟上考古。

该区域的地面坚硬如石、平整如镜。

管理人员为方便,建立了标准的直角坐标系。

每个机器人都各有特长、身怀绝技。

它们感兴趣的内容也不相同。

经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。

矩形的表示格式为 (x1,y1,x2,y2),代表矩形的两个对角点坐标。

为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。

小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。

其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。

注意,各个矩形间可能重叠。

输入格式
第一行,一个整数 n,表示有多少个矩形。

接下来的 n 行,每行有 4 个整数 x1,y1,x2,y2,空格分开,表示矩形的两个对角顶点坐标。

输出格式
一行一个整数,表示矩形覆盖的总面积。

数据范围
1≤n≤10000,
0≤x1,x2,y2,y2≤10000
数据保证 x1<x2 且 y1<y2。

输入样例1:
3
1 5 10 10
3 1 20 20
2 7 15 17
输出样例1:
340
输入样例2:
3
5 2 10 6
2 7 12 10
8 1 15 15
输出样例2:
128

c++代码加注释:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10010;

int n;
//线段结构体,包含竖线的x,y1,y2三个位置信息
//k表示这条线段是一个矩形的开始还是结束
//结构体数组用来存储所有线段,因为还要先对所有线段排序,按x从小到大的顺序来扫描
struct Segment
{
    int x, y1, y2;
    int k;
    bool operator< (const Segment &t)const
    {
        return x < t.x;
    }
}seg[N * 2];

//线段树结点存储的是y轴方向区间的被覆盖长度和被覆盖次数
//l,r分别表示当前区间的端点
//cnt表示结点区间被覆盖次数,len表示结点区间内被覆盖的长度
//len是真正关心的,cnt相当于一个标记用来正确求出len
struct Node
{
    int l, r;
    int cnt, len;
}tr[N * 4];

//递归
void pushup(int u)
{
    //结点区间被覆盖过一次以上,那len就等于区间长度
    //+1是因为线段树的叶节点是单位线段,而不是点,比如区间包含单位线段1和单位线段2,len=2-1+1=2
    if (tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1;
    //否则递归下去,终止条件:当l==r表示是一条单位线段,且没被覆盖(else),所以len=0
    else if (tr[u].l == tr[u].r) tr[u].len = 0;
    //不是叶节点len=两个子节点的len之和
    else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}

//递归
void build(int u, int l, int r)
{
    //初始化,因为是边扫描边维护线段树,所以一开始相当于空白区域,全部区间都没有覆盖
    //cnt和len也应该置零的,但是全局数组默认置零了
    tr[u] = {l, r};
    if (l == r) return;
    //递归下去,完成初始化操作
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

//修改结点中l到r区间的cnt
void modify(int u, int l, int r, int k)
{
    //区间完全包含结点,直接修改结点的cnt,修改完cnt会影响到当前节点区间的覆盖性
    //即len需要重新计算,调用pushup
    if (tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].cnt += k;
        pushup(u);
    }
    //区间不完全覆盖结点,递归下去修改子节点,然后pushup更新
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, k);
        if (r > mid) modify(u << 1 | 1, l, r, k);
        pushup(u);
    }
}




int main()
{
    scanf("%d", &n);
    int m = 0;
    for (int i = 0; i < n; i ++ )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        seg[m ++ ] = {x1, y1, y2, 1};
        seg[m ++ ] = {x2, y1, y2, -1};
    }

    sort(seg, seg + m);

    build(1, 0, 10000);

    int res = 0;
    
    //扫描线法,从左到右扫描竖线,每次扫描到一条竖线,都会修改当前线段树的状态
    for (int i = 0; i < m; i ++ )
    {
        if (i > 0) res += tr[1].len * (seg[i].x - seg[i - 1].x);
        modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k);
    }
    
    printf("%d\n", res);

    return 0;
}


java代码:

import java.util.*;

public class Main
{
	static int N=10010,n,x1,x2,y1,y2,m,res;
	static class segment
	{
		int x,y1,y2,k;
		public segment(int x,int y1,int y2,int k)
		{
			this.x=x;
			this.y1=y1;
			this.y2=y2;
			this.k=k;
		}
	}
	static segment seg[]=new segment[N*2];
	static class node
	{
		int l,r,cnt,len;
	}
	static class mcomp implements Comparator<segment>
	{

		@Override
		public int compare(segment o1, segment o2) {
			return o1.x-o2.x;
		}
		
	}
	static node tr[]=new node[4*N];
	static void pushup(int u)
	{
		if(tr[u].cnt>0)tr[u].len=tr[u].r-tr[u].l+1;
		else if(tr[u].l==tr[u].r)
		{
			tr[u].len=0;
		}
		else {
			tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
		}
	}
	static void build(int u,int l,int r)
	{
		tr[u]=new node();
		//类成员对象默认值为0,所以不用特地给cnt和len赋值
		tr[u].l=l;
		tr[u].r=r;
		if(l==r)return;
		int mid=l+r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
	}
	static void modify(int u,int l,int r,int k)
	{
		if(tr[u].l>=l&&tr[u].r<=r)
		{
			tr[u].cnt+=k;
			pushup(u);
		}
		else 
		{
			int mid=tr[u].l+tr[u].r>>1;
			if(l<=mid)modify(u<<1,l,r,k);
			if(r>mid)modify(u<<1|1,l,r,k);
			pushup(u);
		}
	}
	public static void main(String args[])
	{
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		build(1,0,10000);
		for(int i=0;i<n;++i)
		{
			x1=sc.nextInt();y1=sc.nextInt();
			x2=sc.nextInt();y2=sc.nextInt();
			
			seg[m++]=new segment(x1,y1,y2,1);
			seg[m++]=new segment(x2,y1,y2,-1);
		}
		Arrays.sort(seg,0,m,new mcomp());
		for(int i=0;i<m;++i)
		{
			if(i>0)res+=(seg[i].x-seg[i-1].x)*tr[1].len;
			modify(1,seg[i].y1,seg[i].y2-1,seg[i].k);
		}
		System.out.println(res);
	}
}


二刷

import java.util.*;

public class Main
{
	static int N=10010,n,x1,x2,y1,y2,m,res;
	static class segment
	{
		int x,y1,y2,k;
		public segment(int x,int y1,int y2,int k)
		{
			this.x=x;
			this.y1=y1;
			this.y2=y2;
			this.k=k;
		}
	}
	static segment seg[]=new segment[N*2];
	static class node
	{
		int l,r,cnt,len;
	}
	static class mcomp implements Comparator<segment>
	{

		@Override
		public int compare(segment a, segment b) {
			return a.x-b.x;
		}
	}
	static node tr[]=new node[N*4];
	
	static void pushup(int u)
	{
		if(tr[u].cnt>0)tr[u].len=tr[u].r-tr[u].l+1;
		else if(tr[u].l==tr[u].r){
			tr[u].len=0; //未被覆盖
		}
		else {
			tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
		}
	}
	static void build(int u,int l,int r)
	{
		tr[u]=new node();
		tr[u].l=l;
		tr[u].r=r;
		if(l==r)return ;
		int mid=l+r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
	}
	static void modify(int u,int l,int r,int k)
	{
		if(tr[u].l==tr[u].r)
		{
			tr[u].cnt+=k;
			pushup(u);
		}
		else 
		{
			int mid=tr[u].l+tr[u].r>>1;
			if(l<=mid)modify(u<<1,l,r,k);
			if(r>mid)modify(u<<1|1,l,r,k);
			pushup(u);
		}
	}
	public static void main(String args[]){
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		build(1,0,10000-1);
		for(int i=0;i<n;++i)
		{
			x1=sc.nextInt();y1=sc.nextInt();
			x2=sc.nextInt();y2=sc.nextInt();
			seg[m++]=new segment(x1,y1,y2,1);
			seg[m++]=new segment(x2,y1,y2,-1);
		}
		Arrays.sort(seg,0,m,new mcomp());
		for(int i=0;i<m;++i)
		{
			if(i>0)res+=(seg[i].x-seg[i-1].x)*tr[1].len;
			modify(1,seg[i].y1,seg[i].y2-1,seg[i].k);
		}
		System.out.println(res);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wow_awsl_qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值