POJ-3470 Walls

探讨了在无限大的二维平面上,鸟如何选择最近的墙进行撞击的问题,通过离散化和扫描线算法,计算每堵墙被鸟撞击的次数。

POJ-3470 Walls

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<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
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;
}
本系统旨在构建一套面向高等院校的综合性教务管理平台,涵盖学生、教师及教务处三个核心角色的业务需求。系统设计着重于实现教学流程的规范化与数据处理的自动化,以提升日常教学管理工作的效率与准确性。 在面向学生的功能模块中,系统提供了课程选修服务,学生可依据培养方案选择相应课程,并生成个人专属的课表。成绩查询功能支持学生查阅个人各科目成绩,同时系统可自动计算并展示该课程的全班最高分、平均分、最低分以及学生在班级内的成绩排名。 教师端功能主要围绕课程与成绩管理展开。教师可发起课程设置申请,提交包括课程编码、课程名称、学分学时、课程概述在内的新课程信息,亦可对已开设课程的信息进行更新或撤销。在课程管理方面,教师具备录入所授课程期末考试成绩的权限,并可导出选修该课程的学生名单。 教务处作为管理中枢,拥有课程审批与教学统筹两大核心职能。课程设置审批模块负责处理教师提交的课程申请,管理员可根据教学计划与资源情况进行审核批复。教学安排模块则负责全局管控,包括管理所有学生的选课最终结果、生成包含学号、姓名、课程及成绩的正式成绩单,并能基于选课与成绩数据,统计各门课程的实际选课人数、最高分、最低分、平均分以及成绩合格的学生数量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值