一、题面

POJ3470

二、分析

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 【离散化+扫描线+线段树】的更多相关文章

  1. hdu 4419 Colourful Rectangle (离散化扫描线线段树)

    Problem - 4419 题意不难,红绿蓝三种颜色覆盖在平面上,不同颜色的区域相交会产生新的颜色,求每一种颜色的面积大小. 比较明显,这题要从矩形面积并的方向出发.如果做过矩形面积并的题,用线段树 ...

  2. poj 1151 Atlantis (离散化 + 扫描线 + 线段树 矩形面积并)

    题目链接题意:给定n个矩形,求面积并,分别给矩形左上角的坐标和右上角的坐标. 分析: 映射到y轴,并且记录下每个的y坐标,并对y坐标进行离散. 然后按照x从左向右扫描. #include <io ...

  3. 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 ...

  4. HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  5. 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

    [BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...

  6. 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树

    题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...

  7. POJ 1151 扫描线 线段树

    题意:给定平面直角坐标系中的N个矩形,求它们的面积并. 题解:建立一个四元组(x,y1,y2,k).(假设y1<y2)用来储存每一条线,将每一条线按x坐标排序.记录所有的y坐标以后排序离散化.离 ...

  8. hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

    题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...

  9. P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)

    题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...

随机推荐

  1. 2.QWidget类

    简述: QWidget类是所有用户界面对象的基类. Widget是用户界面的基本单元:它从窗口系统接收鼠标,键盘和其他事件,并在屏幕上绘制自己. 每个Widget都是矩形的,它们按照Z-order进行 ...

  2. (模拟)Arithmetic Sequence -- HDU -- 5400

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=5400 Time Limit: 4000/2000 MS (Java/Others)    Memory ...

  3. Android-隐式意图激活操作系统通话界面

    阅读Android操作系统的 packages/apps/phone/AndroidManifest.xml,是如何暴露的   AndroidManifest.xml Android操作系统在这里明确 ...

  4. Android-SqliteSQL语句大全

    SqliteSQL语句大全 创表语句: create table student_table(_id integer primary key autoincrement, name text, age ...

  5. ASP.NET Core2读写InfluxDB时序数据库

    在我们很多应用中会遇到有一种基于一系列时间的数据需要处理,通过时间的顺序可以将这些数据点连成线,再通过数据统计后可以做成多纬度的报表,也可通过机器学习来实现数据的预测告警.而时序数据库就是用于存放管理 ...

  6. MSP430 G2553 LaunchPad设置GPIO

    一. 背景知识:逻辑运算符的使用 当程序初始化时,对于复位状态有不确定性的寄存器(如PxOUT),建议采用直接赋值:其他情况下最好使用逻辑运算符修改寄存器. 直接赋值 REGISTER = 0b111 ...

  7. TSQL--使用CTE完成递归查询

    CREATE TABLE TB001( CategoryId INT PRIMARY KEY, ParentCategoryId INT, CategoryName NVARCHAR(200))GO ...

  8. Solr之functionQuery(函数查询)

    Solr函数查询 让我们可以利用 numeric域的值 或者 与域相关的的某个特定的值的函数,来对文档进行评分. 怎样使用函数查询 这里主要有两种方法可以使用函数查询,这两种方法都是通过solr ht ...

  9. mysql 数据库或者表空间使用查询

    直接上语句 查所有数据库占用空间大小 select TABLE_SCHEMA, concat(truncate(sum(data_length)/1024/1024,2),' MB') as data ...

  10. vim命令以及gcc编译器的常用cmd

    Gcc常用命令:         -c    仅对源文件进行编译,不链接生成可执行文件.常用于查错和只生成目标文件.     -o    经过gcc处理过后的结果保存在-o后面的文件中,可以是多种文件 ...