POJ_3470 Walls 【离散化+扫描线+线段树】
一、题面
二、分析
POJ感觉是真的老了。
这题需要一些预备知识:扫描线,离散化,线段树。线段树是解题的关键,因为这里充分利用了线段树区间修改的高效性,再加上一个单点查询。
为什么需要离散化?
坐标太分散了,据说可以到 long long,但是就这么多个点,所以离散化一下,方便处理。
为什么用扫描线算法?
用扫描线,可以方便的求出一个bird到wall的距离,因为是顺着一个方向扫的(这题需要从四个方向扫),所以保证了准确性和高效性。
为什么用线段树?
用线段树非常明显,因为墙有这么多,并且结合扫描线,墙可能会有重叠部分,而线段树的lazy标记用法刚好可以完美的使用。
然后就是一些思维方面的了,因为bird可以朝4个方向飞,所以需要从4个方向扫,如果不从4个方向扫,每次处理一个bird非常不好处理。
然后就是写代码的时候要细心,可能有很多细节问题。
三、AC代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <cstring>
#include <cmath>
using namespace std; #define Lson(x) (x<<1) //左儿子
#define Rson(x) (x<<1|1) //左儿子
#define Mid(l, r) ( (l+r)>>1 )
#define LL long long
const int MAXN = 2e5 + ;
int N, M; //N is the number of wall
int ans[MAXN];
vector<LL> cor_x, cor_y;
struct Wall
{
LL x1, y1;
LL x2, y2;
}W[MAXN];
struct Bird
{
LL x, y;
}B[MAXN];
struct Dist
{
int id;
LL dist;
Dist()
{
//这里必须用long long 的无穷大,因为给的数据会超过int
//找这个WA点找了很久
dist = __LONG_LONG_MAX__;
}
}D[MAXN];
enum Type
{
WALL, BIRD
}; struct Object //处理对象
{
Type type;
int id; //bird's or wall's id
int x, y1, y2;
Object(int x, int y1, int y2, int id, Type type):x(x),y1(y1),y2(y2),id(id),type(type){}
bool operator < (const Object &t)const
{
return x < t.x;
}
}; struct SegTree
{
//Value的值表示线段树维护区间的墙的id
int Left[MAXN<<], Right[MAXN<<], Value[MAXN<<];
//线段树初始化
void init(int p, int l, int r)
{
Left[p] = l;
Right[p] = r;
Value[p] = ;
if(l == r)
return;
init(Lson(p), l, Mid(l, r));
init(Rson(p), Mid(l, r) + , r);
}
//出现了墙,则维护区间
void update(int p, int l, int r, int id)
{
if(Left[p] == l && Right[p] == r)
{
Value[p] = id;
return;
}
if(Value[p] > )
{
Value[Lson(p)] = Value[p];
Value[Rson(p)] = Value[p];
Value[p] = ; //一定记得清0
}
// int mid = Mid(l, r);
int mid = Mid(Left[p], Right[p]);
if(l > mid)
{
update(Rson(p), l, r, id);
}
else if(r <= mid)
{
update(Lson(p), l, r, id);
}
else
{
update(Lson(p), l, mid, id);
update(Rson(p), mid + , r, id);
}
}
//出现了bird, 在线段树中寻找合适的wall被撞
// loc 如果是沿x轴飞,那么loc应该是y值,只有这样才能保证撞的有效
int find(int p, int loc)
{
if(Left[p] == Right[p] && Left[p] == loc)
return Value[p];
if(Value[p] > )
{
Value[Lson(p)] = Value[p];
Value[Rson(p)] = Value[p];
Value[p] = ; //一定记得清0
}
int mid = Mid(Left[p], Right[p]);
if(loc > mid)
{
return find(Rson(p), loc);
}
else
{
return find(Lson(p), loc);
}
}
}STree; //计算距离
LL cal_dis(bool dir, int x1, int x2)
{
LL d1, d2;
if(dir)
{
d1 = cor_x[x1 - ];
d2 = cor_x[x2 - ];
}
else
{
d1 = cor_y[x1 - ];
d2 = cor_y[x2 - ];
}
d1 = d1 - d2;
if(d1 < )
return -d1;
else
return d1;
} void scan(const vector<Object> &arr, bool dir) //dir表示方向
{
STree.init(, , max(cor_x.size(), cor_y.size()) + );
vector<Object>::const_iterator itr;
for(itr = arr.begin(); itr != arr.end(); itr++)
{
if(itr->type == WALL)
{
STree.update(, itr->y1, itr->y2, itr->id);
}
else
{
int pos = STree.find(, itr->y1); if(pos)
{//一定要保证墙存在!
LL len;
if(dir) // dir = 1 表示沿x轴方向
//len = cal_dis(dir, itr->x, W[pos].x1);
len = min( cal_dis(true, W[pos].x1, itr->x), cal_dis(true, W[pos].x2, itr->x) ); else
//len = cal_dis(dir, itr->x, W[pos].y1);
len = min( cal_dis(false, W[pos].y1, itr->x), cal_dis(false, W[pos].y2, itr->x) );
if(len < D[itr->id].dist)
{
D[itr->id].dist = len;
D[itr->id].id = pos;
}
}
}
} } void fly_x() //假设现在bird都是沿着x轴方向飞行
{
vector<Object> T; //存储扫描时要处理的对象
for(int i = ; i <= N; i++)
{
T.push_back(Object(W[i].x1, W[i].y1, W[i].y2, i, WALL) );
if(W[i].x1 != W[i].x2) //必须要加末端,因为bird可能在延长线上
{
T.push_back(Object(W[i].x2, W[i].y1, W[i].y2, i, WALL) );
}
}
for(int i = ; i <= M; i++)
{
T.push_back(Object(B[i].x, B[i].y, , i, BIRD) );
}
sort(T.begin(), T.end());
scan(T, true);
reverse(T.begin(), T.end());
scan(T, true);
} void fly_y() //假设现在bird都是沿着y轴方向飞行
{
vector<Object> T; //存储扫描时要处理的对象
for(int i = ; i <= N; i++)
{
T.push_back(Object(W[i].y1, W[i].x1, W[i].x2, i, WALL) );
if(W[i].y1 != W[i].y2)
{
T.push_back(Object(W[i].y2, W[i].x1, W[i].x2, i, WALL) );
}
}
for(int i = ; i <= M; i++)
{
T.push_back(Object(B[i].y, B[i].x, , i, BIRD) ); //注意顺序
}
sort(T.begin(), T.end());
scan(T, false);
reverse(T.begin(), T.end());
scan(T, false);
} void discretization() //离散化处理
{
sort(cor_x.begin(), cor_x.end());
sort(cor_y.begin(), cor_y.end());
cor_x.erase(unique(cor_x.begin(), cor_x.end() ), cor_x.end() );
cor_y.erase(unique(cor_y.begin(), cor_y.end() ), cor_y.end() );
for(int i = ; i <= N; i++)
{
W[i].x1 = lower_bound(cor_x.begin(), cor_x.end(), W[i].x1) - cor_x.begin() + ;
W[i].x2 = lower_bound(cor_x.begin(), cor_x.end(), W[i].x2) - cor_x.begin() + ;
W[i].y1 = lower_bound(cor_y.begin(), cor_y.end(), W[i].y1) - cor_y.begin() + ;
W[i].y2 = lower_bound(cor_y.begin(), cor_y.end(), W[i].y2) - cor_y.begin() + ;
}
for(int i = ; i <= M; i++)
{
B[i].x = lower_bound(cor_x.begin(), cor_x.end(), B[i].x) - cor_x.begin() + ;
B[i].y = lower_bound(cor_y.begin(), cor_y.end(), B[i].y) - cor_y.begin() + ;
}
} int main()
{ //freopen("input.txt", "r", stdin);
scanf("%d %d", &N, &M);
for(int i = ; i <= N; i++)
{
scanf("%I64d %I64d %I64d %I64d", &W[i].x1, &W[i].y1, &W[i].x2, &W[i].y2);
if(W[i].x1 > W[i].x2) swap(W[i].x1, W[i].x2);
if(W[i].y1 > W[i].y2) swap(W[i].y1, W[i].y2);
cor_x.push_back(W[i].x1);
cor_x.push_back(W[i].x2);
cor_y.push_back(W[i].y1);
cor_y.push_back(W[i].y2);
}
for(int j = ; j <= M; j++)
{
scanf("%I64d %I64d", &B[j].x, &B[j].y);
cor_x.push_back(B[j].x);
cor_y.push_back(B[j].y);
}
discretization();
fly_x();
fly_y();
memset(ans, , sizeof(ans));
for(int i = ; i <= M; i++)
{
ans[ D[i].id ]++;
}
for(int i = ; i <= N; i++)
{
printf("%d\n", ans[i]);
}
return ;
}
POJ_3470 Walls 【离散化+扫描线+线段树】的更多相关文章
- hdu 4419 Colourful Rectangle (离散化扫描线线段树)
Problem - 4419 题意不难,红绿蓝三种颜色覆盖在平面上,不同颜色的区域相交会产生新的颜色,求每一种颜色的面积大小. 比较明显,这题要从矩形面积并的方向出发.如果做过矩形面积并的题,用线段树 ...
- poj 1151 Atlantis (离散化 + 扫描线 + 线段树 矩形面积并)
题目链接题意:给定n个矩形,求面积并,分别给矩形左上角的坐标和右上角的坐标. 分析: 映射到y轴,并且记录下每个的y坐标,并对y坐标进行离散. 然后按照x从左向右扫描. #include <io ...
- HDU 3265/POJ 3832 Posters(扫描线+线段树)(2009 Asia Ningbo Regional)
Description Ted has a new house with a huge window. In this big summer, Ted decides to decorate the ...
- HDU 3642 - Get The Treasury - [加强版扫描线+线段树]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...
- 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树
[BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...
- 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树
题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...
- POJ 1151 扫描线 线段树
题意:给定平面直角坐标系中的N个矩形,求它们的面积并. 题解:建立一个四元组(x,y1,y2,k).(假设y1<y2)用来储存每一条线,将每一条线按x坐标排序.记录所有的y坐标以后排序离散化.离 ...
- hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积
题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...
- P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)
题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...
随机推荐
- Mosquitto服务器的搭建以及SSL/TLS安全通信配置
Mosquitto服务器的搭建以及SSL/TLS安全通信配置 摘自:https://segmentfault.com/a/1190000005079300 openhab raspberry-pi ...
- 怎样application不被第三方应用杀掉--Android
方法: 对于放在/system/app下的应用,需要在其Manifest.xml文件中设置persistent属性,如应用程序'Phone'的AndroidManifest.xml文件: <ap ...
- Spring MVC之RequestMappingHandlerAdapter初始化
RequestMappingHandlerAdapter基于注解的处理器适配器,目的是用来执行handler,同时返回modelAndView给前端控制器,这块个人感觉是spring mvc的核心了, ...
- Android文件上传
服务端: using System; using System.Collections.Generic; using System.Linq; using System.Web; using Syst ...
- Access denied for user 'root'@'MiWiFi-Ryyy-srv' (using password: YES)
虽然是跟很多人一样的问题但是原因不同,其他很多文章说是授权问题,也确实是授权问题,但是,配置文件写的是连接localhost,而这里不知道什么原因切换了使用的用户,变成了默认访问MiWiFi-Ryyy ...
- C++ 数据封装和抽象
C++ 数据抽象 数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节. 数据抽象是一种依赖于接口和实现分离的编程(设计)技术. 让我们举一个现实生活中的真实例子 ...
- HBase-1.2.1和Phoenix-4.7.0分布式安装指南
目录 目录 1 1. 前言 2 2. 概念 2 2.1. Region name 2 3. 约定 2 4. 相关端口 3 5. 下载HBase 3 6. 安装步骤 3 6.1. 修改conf/regi ...
- 课堂限时训练-简易计算器·mini dc
课堂限时训练-简易计算器·mini dc 实验题目 采用后缀表达式法,设计一个建议计算器,实现+.-.*./四种运算. 代码实现 码云链接 关键代码部分及结果如下: 实验分析 首先,分析一下后缀表达式 ...
- Ubuntu的常识使用了解2
1. 在linux系统中,所有的目录(分区)都是挂靠在/跟目录下. 使用「cd」这个指令来切換目录.切换目录的方式:(1)绝对路径,以根目录做开头/, (2)相对目录, 没有以根目录开头. 注意:插补 ...
- 4、Docker的安装
docker官方安装文档 Mac上安装Docker Install Docker for Mac | Docker Documentation Windows安装Docker Instal ...