Uva1398--Meteor(扫描线)

题目大意:今晚有流星划过,求在某瞬间最多有多少流星能被拍进镜头?


分析:咋看之下有点复杂。一点点分析后,可以发现这题是借助扫描线来解决,类似区间并,面积并。

每个流星可以看做一个开区间,这个问题可以转化为,求扫描线在哪个位置时与最多的开区间相交。

首先,我们需要将流星转化为开区间,如何转化呢?题目给的数据有坐标x,y,以及xy方向上的速度,那么我们很自然地想到分解成xy两个方向上来求区间,然后得到的两个区间的相交部分,就是该流星的开区间。

然后,就可以用扫描线啦~


需要注意的是,当某一位置既有前一个区间的右端点,又有后一个区间的左端点时,我们先操作右端点,再操作左端点。很好理解,因为在镜头边缘的点,我们并不计数。



代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 111111;

struct Event {
    int x;
    int type;
    bool operator < (const Event & cmp) {
        return x < cmp.x || (x == cmp.x && type > cmp.type);
    }
}e[2*maxn];

void update(int a, int x, int w, int& L, int& R) {
    if(a == 0) {
        if(x <= 0|| x >= w) R = L-1;
    }
    else if(a > 0) {
        L = max(L, -x*2520/a);
        R = min(R, (w-x)*2520/a);
    }
    else {
        L = max(L, (w-x)*2520/a);
        R = min(R, -x*2520/a);
    }
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, w, h;
        int a, b, x, y;
        scanf("%d%d%d", &w, &h, &n);
        int cnt = 0;
        for(int i = 0; i < n; i++) {
            scanf("%d%d%d%d", &x, &y, &a, &b);
            int L = 0, R = 1 << 30;
            update(a, x, w, L, R);
            update(b, y, h, L, R);
            if(R > L) {
                e[cnt++] = (Event){L, 0};
                e[cnt++] = (Event){R, 1};
            }
        }
        sort(e, e+cnt);
        int ans = 0;
        int tt = 0;
        for(int i = 0; i < cnt; i++) {
            if(e[i].type == 0) {
                tt++;
                ans = max(ans, tt);
            }
            else tt--;
        }
        printf("%d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(Uva1398--Meteor(扫描线))