Time Limit: 6000MS Memory Limit: 131072K
Total Submissions: 2624 Accepted: 402
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
Source
POJ Monthly–2007.11.25, Zhou Dong
一个无限大的二维平面上有n(1<=n<=50,000)堵墙,m(1<=m<=50,000)只鸟。给出墙端点坐标,鸟坐标,墙一定是平行于坐标轴,横着或竖着。每只鸟都会选一个离自己最近的墙撞过去,鸟一定是平行于坐标轴飞行,只能横着或竖着飞,问每堵墙被几只鸟撞过。
坐标可以为负数
鸟可能初始就卡在某堵墙里(坐标在墙的端点或连线上,给这堵墙算一次)
鸟可以在墙的延长线上,可以直接顺着延长线撞到墙上
鸟不会有多个墙来选择
鸟有可能撞不到任何墙
因为n,m<=5e4,所以先离散化。然后用扫描线。以从上到下为例。扫描线从上到下,相对的鸟就是从下往上飞。
每遇到一个墙壁就把代表它的区间添加到线段树里。线段树每个节点的值v表示墙壁的序号。
v = 0时表示该节点代表的区间没有被覆盖,v = -1表示该区间有多个墙壁。v>0表示 纵坐标>=yi的墙壁中,距离y = yi这条线最近的墙壁序号是v。每当一行墙壁添加完了,就计算该行与下一行之间的鸟距离墙壁的距离。用单点查询,获得离鸟最近的墙壁的编号,然后反离散化,求得原来的坐标,相互减一下即可。
四个方向全部做一遍,去最小距离的墙的编号。最后统计一遍即可。
#include
#include
#include
#include
using namespace std;
const int N = 5e4+10;
struct point{
int x,y;
int d,index;
}b[N];
struct seg{
point st,ed;
int index;
}a[N];
//离散化。mx[i],my[i]为第i个点离散化后的x,y坐标,rx,ry为反离散化的坐标。
int x[N<<2],y[N<<2],lenx,leny,rx[N<<2],mx[N<<2],my[N<<2],ry[N<<2],r[N<<2],n,m;
int *fi;
bool cmp1(const int &l,const int &r){return fi[l] < fi[r];}
void mapping(int *data,int n,int *mp,int *rx)
{
for (int i = 1;i<=n;i++) r[i] = i;
fi = data;
sort(r+1,r+1+n,cmp1);
rx[mp[r[1]] = 1] = data[r[1]];
for (int i = 2,cnt = 1,last = data[r[1]];i<=n;i++)
{
if (last != data[r[i]]) cnt++;
last = data[r[i]];
rx[mp[r[i]] = cnt] = data[r[i]];
}
}
struct node{
int lazy,v;
};
struct seg_tree{
node tree[N<<4];
void pushdown(int x)
{
if (tree[x].lazy > 0)
{
tree[x<<1].v = tree[x<<1].lazy = tree[x<<1|1].v = tree[x<<1|1].lazy = tree[x].lazy;
tree[x].lazy = 0;
}
}
void merge(int x)
{
if (tree[x<<1].v == tree[x<<1|1].v) tree[x].v = tree[x<<1].v;
else tree[x].v = -1;
}
void add(int st,int ed,int l,int r,int x,int c)
{
if (st <= l && r <= ed)
{
tree[x].v = c;
tree[x].lazy = c;
return;
}
pushdown(x);
tree[x].v = -1;
int mid = (l + r) / 2;
if (st <= mid) add(st,ed,l,mid,x<<1,c);
if (ed > mid) add(st,ed,mid+1,r,x<<1|1,c);
merge(x);
}
int query(int p,int l,int r,int x)
{
if (l == r || (l<=p && p<=r && tree[x].v != -1)) return tree[x].v;
pushdown(x);
int mid = (l + r) / 2;
if (p <= mid) return query(p,l,mid,x<<1);
else return query(p,mid+1,r,x<<1|1);
}
void clear()
{
memset(tree,0,sizeof(tree));
}
}T;
int wall[N<<2],birds[N<<2];
//纵向的扫描
bool check(int ptr,int i,bool c)
{
if (i > n) return 1;
if (c) return b[birds[ptr]].y > a[wall[i]].st.y;//从上往下
else return b[birds[ptr]].y < a[wall[i]].st.y;//从下往上
}
//dis为y的反离散化的数组,border为x方向的最大宽度,c == 1表示从上往下,c==0表示从下往上
void scan(int *dis,int border,bool c)
{
for (int i = 1,ptr = 1;i<=n;)
{
T.add(a[wall[i]].st.x,a[wall[i]].ed.x,1,border,1,a[wall[i]].index); i++;
while (i<=n && a[wall[i-1]].st.y == a[wall[i]].st.y)//添加一列墙壁
{
T.add(a[wall[i]].st.x,a[wall[i]].ed.x,1,border,1,a[wall[i]].index);
i++;
}
while (ptr <= m && check(ptr,i,c))//计算该行与下一行之间的鸟
{
int num = T.query(b[birds[ptr]].x,1,border,1);
if (num == 0)//没有墙壁覆盖
{
ptr++; continue;
}
int dif;
if (a[num].st.y == a[num].ed.y) dif = abs(dis[a[num].st.y] - dis[b[birds[ptr]].y]);//墙壁是横的
else dif = abs(dis[a[num].ed.y] - dis[b[birds[ptr]].y]);//墙壁是竖的
if (b[birds[ptr]].index == 0 || dif < b[birds[ptr]].d) b[birds[ptr]].d = dif,b[birds[ptr]].index = num;
ptr++;
}
}
}
bool cmpy(const int &l,const int &r){return a[l].st.y > a[r].st.y;}
bool cmpy1(const int &l,const int &r){return b[l].y > b[r].y;}
bool cmpy2(const int &l,const int &r){return a[l].st.y < a[r].st.y;}
bool cmpy3(const int &l,const int &r){return b[l].y < b[r].y;}
//横向的扫描
bool check1(int ptr,int i,bool c)
{
if (i > n) return 1;
if (c) return b[birds[ptr]].x < a[wall[i]].st.x;//从左到右
else return b[birds[ptr]].x > a[wall[i]].st.x;//从右到左
}
//dis为x的反离散化的数组,border为y方向的最大宽度,c == 1表示从左到右,c==0表示从右到左
void scan1(int *dis,int border,bool c)
{
for (int i = 1,ptr = 1;i<=n;)
{
T.add(a[wall[i]].st.y,a[wall[i]].ed.y,1,border,1,a[wall[i]].index); i++;
while (i<=n && a[wall[i-1]].st.x == a[wall[i]].st.x)//添加一列墙壁
{
T.add(a[wall[i]].st.y,a[wall[i]].ed.y,1,border,1,a[wall[i]].index);
i++;
}
while (ptr <= m && check1(ptr,i,c))//计算该列与下一列之间的鸟
{
int num = T.query(b[birds[ptr]].y,1,border,1);
if (num == 0)//没有墙壁覆盖
{
ptr++; continue;
}
int dif;
if (a[num].st.x == a[num].ed.x) dif = abs(dis[a[num].st.x] - dis[b[birds[ptr]].x]);//墙壁是竖的
else dif = abs(dis[a[num].ed.x] - dis[b[birds[ptr]].x]);//墙壁是横的
if (b[birds[ptr]].index == 0 || dif < b[birds[ptr]].d) b[birds[ptr]].d = dif,b[birds[ptr]].index = num;
ptr++;
}
}
}
bool cmpx(const int &l,const int &r){return a[l].st.x < a[r].st.x;}
bool cmpx1(const int &l,const int &r){return b[l].x < b[r].x;}
bool cmpx2(const int &l,const int &r){return a[l].st.x > a[r].st.x;}
bool cmpx3(const int &l,const int &r){return b[l].x > b[r].x;}
int maxx = 0,maxy = 0;
void solve()
{
//从上到下
for (int i = 1;i<=n;i++) wall[i] = i;
for (int i = 1;i<=m;i++) birds[i] = i;
sort(wall+1,wall+n+1,cmpy);
sort(birds+1,birds+m+1,cmpy1);
scan(ry,maxx,1);
T.clear();
//从下往上
//将墙壁方向改为从下到上,即开始点的纵坐标小于结束点的纵坐标,用在计算鸟和墙壁之间的距离,保证ed点离鸟更近
for (int i = 1;i<=n;i++)
{
wall[i] = i;
if (a[i].st.y > a[i].ed.y) swap(a[i].st,a[i].ed);
}
for (int i = 1;i<=m;i++) birds[i] = i;
sort(wall+1,wall+n+1,cmpy2);
sort(birds+1,birds+m+1,cmpy3);
scan(ry,maxx,0);
T.clear();
//从左到右
//将墙壁方向改为从左到右
for (int i = 1;i<=n;i++)
{
wall[i] = i;
if (a[i].st.x > a[i].ed.x) swap(a[i].st,a[i].ed);
}
for (int i = 1;i<=m;i++) birds[i] = i;
sort(wall+1,wall+n+1,cmpx);
sort(birds+1,birds+m+1,cmpx1);
scan1(rx,maxy,1);
T.clear();
//从右到左
//将墙壁方向改为从右到左
for (int i = 1;i<=n;i++)
{
wall[i] = i;
if (a[i].st.x < a[i].ed.x) swap(a[i].st,a[i].ed);
}
for (int i = 1;i<=m;i++) birds[i] = i;
sort(wall+1,wall+n+1,cmpx2);
sort(birds+1,birds+m+1,cmpx3);
scan1(rx,maxy,0);
}
int ans[N];
int main()
{
scanf("%d%d",&n,&m);
lenx = leny = 0;
for (int i = 1;i<=n;i++)
{
scanf("%d%d",&x[++lenx],&y[++leny]);
scanf("%d%d",&x[++lenx],&y[++leny]);
}
for (int i = 1;i<=m;i++) scanf("%d%d",&x[++lenx],&y[++leny]);
//离散化
mapping(x,lenx,mx,rx);
mapping(y,leny,my,ry);
for (int i = 1,len = 0;i<=n;i++)
{
a[i].st.x = mx[++len];
a[i].st.y = my[len];
a[i].ed.x = mx[++len];
a[i].ed.y = my[len];
maxx = max(max(maxx,a[i].st.x),a[i].ed.x);
maxy = max(max(maxy,a[i].st.y),a[i].ed.y);
if (a[i].st.x == a[i].ed.x && a[i].st.y < a[i].ed.y) swap(a[i].st,a[i].ed);
else if (a[i].st.y == a[i].ed.y && a[i].st.x > a[i].ed.x) swap(a[i].st,a[i].ed);
a[i].index = i;
// printf("%d %d %d %d\n",a[i].st.x,a[i].st.y,a[i].ed.x,a[i].ed.y);
}
for (int i = n + 1,len = n<<1;i<=n + m;i++)
{
b[i - n].x = mx[++len];
b[i - n].y = my[len];
b[i-n].d = b[i-n].index = 0;
maxx = max(maxx,b[i-n].x);
maxy = max(maxy,b[i-n].y);
// printf("%d %d\n",b[i-n].x,b[i-n].y);
}
//扫描线
solve();
for (int i = 1;i<=m;i++) ans[b[i].index]++;
for (int i = 1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}