Time Limit: 6000MS | Memory Limit: 131072K | |
Total Submissions: 1817 | Accepted: 297 | |
Case Time Limit: 3000MS |
Description
There are N walls. A wall has an infinity height, so it looks like a segment in a plane from high sky. Obviously, they don't intersect. Let's take a series of interesting experiments. Everytime, we put a lovely bird called Xiaoniao in the place. Then, she will choose any one of four possible directions paralleled to axes and disregarded anything else but fly forward. It may occur that she touch a wall and fainted. So poor Xiaoniao is, she always choose the direction which made her fainted as early as possible. You're asked to count, how many times did Xiaoniao touched each wall.
It is guaranteed that each time there will be exactly one direction that makes Xiaoniao faints as early as possible. I.E. She won't have no choice to get faint, neither have more than one direction producing the same fainting time. Xiaoniao won't be placed on a wall, either. Touching an end point of a wall is also considered a touch.
Input
The first line contains N and M (both not exceeding 50,000). M is the number we put Xiaoniao in the place.
The following N
lines describe the walls. Each line consists 4 integers x1,
y1, x2, y2, meaning a wall
from (x1,y1) to
(x2,y2). The wall is always parallel to the
coordinate axes.
The next M lines describe where we put Xiaoniao. Each
line consists 2 integers x and y. This means we put Xiaoniao at
the point (x,y).
Output
N lines, i-th line contains one integer that is the number of Xiaoniao touches the i-th wall.
Sample Input
4 4 10 0 10 40 0 40 40 40 10 10 50 10 40 50 40 10 15 12 12 35 35 38 38 15
Sample Output
1 1 1 1
题意:在一个二维坐标平面,放置了N堵墙,每一堵墙都是平行坐标轴放置的,并且现在放置M只鸟到平面的某些位置,每只鸟都很蠢,只会往平行坐标轴的方向飞行(前后左右),且一定会撞到离鸟最近的那堵墙上,题目已经确定了每只鸟一定都会撞到墙,问放了M只鸟过后,每堵墙上分别有多少只鸟的尸体。
思路:首先对图进行离散化,删减没有用的点,减小存储空间压力。之后建立线段树,线段树上的每一个结点维护的值有1:该结点统治的区间范围[l,r],2:该结点所统治的区间被那一块墙给占据了(这里的墙是指:如果当前鸟在该结点统治的区间范围内,这只鸟应该撞向的墙),维护这块墙的下标。当然如果该结点
所统治的区间上被不止一块墙壁给占据了,那么这个结点就不用记录墙的下标了,交给这个结点的儿子来处理,直到一个结点所统治的区间最多只能被一堵墙所占据为止。
之后我们将鸟的坐标以及墙的坐标共同存入一个object对象数组,按扫描方向的远近排好序,分别从上下左右4个方向从“无穷远”开始扫描平面,对应于每一个方向,都建立好一棵线段树,扫描时,每扫描到一块墙,就把它加入到线段树中维护(每一次扫描到新的墙壁,对接下来还未扫描到的鸟来说都是往这个方向飞所能撞
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> #include<vector> #include<cmath> using namespace std; #define LEFT(x) (x<<1)//左儿子 #define RIGHT(x) ((x<<1)|1) //右儿子 #define MID(l,r) ((l+r)>>1) //取区间中点 const int N_MAX = 200000 + 5; int N, M;//N堵墙,M只鸟 vector<int>cor_x, cor_y; int result[N_MAX]; struct Bird { int x; int y; }bird[N_MAX]; struct Wall { int x1, y1; int x2, y2; }wall[N_MAX]; enum Type{ WALL,BIRD }; struct Distance {//记录每一只鸟会撞向的墙的下标以及之间的距离 int id; int d; Distance() { d = INT_MAX;//初始化为正无穷!!!! } }dist[N_MAX]; struct Object {//对象存储体 Type type;//判断该对象是鸟还是墙 int x;//扫描线依据坐标x的变化而进行 int y1, y2; int id;//对象的编号 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 &b)const { return this->x < b.x; } }; struct Segment_tree { int left[N_MAX*10], right[N_MAX*10],value[N_MAX*10]; void init(int p,int l,int r) { left[p] = l; right[p] = r; value[p] = 0;//记录结点p处会碰到的墙壁 if (l<r) {//!!!!!!! init(LEFT(p), l, MID(l, r)); init(RIGHT(p), MID(l, r) + 1, r); } } void set(int p,int l,int r,int id) {//把鸟在区间[l,r]处的会碰到的墙壁改成下标为id的墙 if (left[p] == l&&right[p] == r) { value[p] = id; return; } if (value[p] > 0) {//可能[l,r]区间重叠了p结点的部分区间,这样一来p结点统治的整块区域不会碰到同一块墙壁,value的记录毫无意义,不如让子结点继承来细化墙壁的分配情况 value[LEFT(p)] = value[p]; value[RIGHT(p)] = value[p]; value[p] = 0; } int mid = MID(left[p], right[p]);//!!!! if (l > mid) {//只要修改右儿子所在区间的墙壁即可 set(RIGHT(p), l, r, id); } else if (r <= mid) { set(LEFT(p), l, r, id); } else { set(LEFT(p), l, mid, id); set(RIGHT(p), mid + 1, r, id); } } int get(int p,int l) { if (left[p] == right[p] && left[p] == l) return value[p]; if (value[p] > 0) { value[LEFT(p)] = value[p]; value[RIGHT(p)] = value[p]; value[p] = 0; } int mid = MID(left[p], right[p]); if (l > mid) { return get(RIGHT(p),l);//!!!!!!return } else return get(LEFT(p),l);//!!!!!! } }tree; int cal_distance(bool vertical,int x1,int x2) {//若已经确定了当前最近的墙,计算鸟到墙的距离 if (!vertical) { x1 = cor_x[x1-1]; x2 = cor_x[x2-1]; } else { x1 = cor_y[x1-1]; x2 = cor_y[x2-1]; } return abs(x1 - x2); } void scan(const vector<Object>&obj_arr,bool vertical) {//vertical代表扫描x轴方向还是y轴方向 tree.init(1,1,max(cor_x.size(),cor_y.size())+10); for (vector<Object>::const_iterator it = obj_arr.begin(); it != obj_arr.end();it++) { if (it->type == WALL) { tree.set(1,it->y1,it->y2,it->id); } else { int p = tree.get(1,it->y1); if (p) {//如果墙p存在的话 int d = (vertical == true ? min(cal_distance(true, wall[p].y1, it->x), cal_distance(true, wall[p].y2, it->x)) : min(cal_distance(false, wall[p].x1, it->x), cal_distance(false,wall[p].x2,it->x)));//!!!! if (d < dist[it->id].d) { dist[it->id].d=d; dist[it->id].id=p; } } } } } void fly_x(){//若鸟是往x轴方向飞的,存储相关的对象 vector<Object>obj_arr; for (int i = 1; i <= N;i++) { obj_arr.push_back(Object(wall[i].x1,wall[i].y1,wall[i].y2,i,WALL)); if (wall[i].x1 != wall[i].x2) { obj_arr.push_back(Object(wall[i].x2, wall[i].y1, wall[i].y2, i, WALL)); } } for (int i = 1; i <= M;i++) { obj_arr.push_back(Object(bird[i].x, bird[i].y, 0, i, BIRD)); } sort(obj_arr.begin(),obj_arr.end()); scan(obj_arr, false);//扫描 reverse(obj_arr.begin(), obj_arr.end()); scan(obj_arr, false);//换一个方向再一次扫描 } void fly_y() {//若鸟是往y轴方向飞的,存储相关的对象 vector<Object>obj_arr; for (int i = 1; i <= N; i++) { obj_arr.push_back(Object(wall[i].y1, wall[i].x1, wall[i].x2, i, WALL)); if (wall[i].y1 != wall[i].y2) { obj_arr.push_back(Object(wall[i].y2, wall[i].x1, wall[i].x2, i, WALL)); } } for (int i = 1; i <= M; i++) { obj_arr.push_back(Object(bird[i].y, bird[i].x, 0, i, BIRD)); } sort(obj_arr.begin(), obj_arr.end()); scan(obj_arr, true);//扫描 reverse(obj_arr.begin(), obj_arr.end()); scan(obj_arr, true);//换一个方向再一次扫描 } int main() { scanf("%d%d",&N,&M); for (int i = 1; i <= N;i++) { scanf("%d%d%d%d",&wall[i].x1,&wall[i].y1,&wall[i].x2,&wall[i].y2); if (wall[i].x1 > wall[i].x2)swap(wall[i].x1, wall[i].x2); if (wall[i].y1 > wall[i].y2)swap(wall[i].y1, wall[i].y2); cor_x.push_back(wall[i].x1); cor_x.push_back(wall[i].x2); cor_y.push_back(wall[i].y1); cor_y.push_back(wall[i].y2); } for (int i = 1; i <= M;i++) { scanf("%d%d",&bird[i].x,&bird[i].y); cor_x.push_back(bird[i].x); cor_y.push_back(bird[i].y); } 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 = 1; i <= N;i++) {//离散化后新的坐标 wall[i].x1 = lower_bound(cor_x.begin(),cor_x.end(),wall[i].x1)-cor_x.begin()+1; wall[i].x2 = lower_bound(cor_x.begin(), cor_x.end(), wall[i].x2) - cor_x.begin() + 1; wall[i].y1 = lower_bound(cor_y.begin(), cor_y.end(), wall[i].y1) - cor_y.begin() + 1; wall[i].y2 = lower_bound(cor_y.begin(), cor_y.end(), wall[i].y2) - cor_y.begin() + 1;//!!!! } for (int i = 1; i <= M;i++) {//离散化后新的坐标 bird[i].x = lower_bound(cor_x.begin(), cor_x.end(), bird[i].x) - cor_x.begin() + 1;; bird[i].y= lower_bound(cor_y.begin(), cor_y.end(), bird[i].y) - cor_y.begin() + 1;//!!!!!! } fly_x(); fly_y(); for (int i = 1; i <= M;i++) { result[dist[i].id]++; } for (int i = 1; i <= N;i++) { printf("%d ",result[i]); } return 0; }
到的最近的墙壁),若扫描到鸟,就在线段树中输入鸟的坐标查找鸟应该撞向那一块墙壁。
四个方向都按上述方法扫描一遍最终即能确定每只鸟应该撞向那一块墙;
最后非常感谢hacks博主提供的思路方法以及代码。
AC代码: