轉載自AcCry
題意:原子彈爆炸,一些互不相交的線段,求能輻射到的線段(可以將原子彈爆炸點視爲泛光源)
以輻射源爲中心對周圍的點按照極座標角度進行排序,然後在極座標上使用掃描線方法。
維護一個集合,集合內的元素是與掃描線相交的線段,排序依據是線段與掃描線的交點到輻射源的距離。該集合中的最小元素就是被照射到的線段。有關容器(set)排序依據的說明:
在掃描線運動前後,如果有兩個線段存在於容器中,這兩個線段與掃描線的交點到輻射源的距離遠近關係不會發生變化。若發生變化,表示掃描線運動範圍內兩個線段有交點,與題目提供的已知條件不符。
PS:此題各種重載 各種stl
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <set>
- #include <algorithm>
- using namespace std;
- #define EPS 1e-8
- #define LS0(a) (a << 1)
- #define LS1(a) ((a << 1) | 1)
- const int MAXN = 20010;
- struct Point {
- double x, y;
- Point(double _x = 0.0, double _y = 0.0): x(_x), y(_y) {}
- Point operator + (const Point &b) const {
- return Point(x + b.x, y + b.y);
- }
- Point operator - (const Point &b) const {
- return Point(x - b.x, y - b.y);
- }
- double operator ^ (const Point &b) const {
- return x * b.y - y * b.x;
- }
- bool operator < (const Point &b) const { //逆時針
- return x * b.y < y * b.x;
- }
- void input() {
- scanf("%lf%lf", &x, &y);
- }
- double diso() {
- return sqrt(x * x + y * y);
- }
- }cur,ps[MAXN];
- Point lnlncross_pt(Point aa, Point ad, Point ba, Point bd) { // 求直線交點
- ad = ad - aa;
- bd = bd - ba;
- double tmp = bd ^ ad;
- return Point(
- (ad.x * bd.x * (ba.y - aa.y) + aa.x * bd.x * ad.y - ba.x * ad.x * bd.y) / tmp,
- (ad.y * bd.y * (aa.x - ba.x) + ba.y * ad.y * bd.x - aa.y * bd.y * ad.x) / tmp);
- }
- struct Item { // 掃描線的點類型
- Point *u, *v;
- int type; // 1: 線段起點; 0: 線段終點;
- int sgid; // 線段序號
- Item(Point *_u = NULL, Point *_v = NULL, int _ty = 0, int _id = 0)
- : u(_u), v(_v), type(_ty), sgid(_id) {}
- bool operator < (const Item &b) const {
- if(u == b.u && v == b.v)
- return false;
- Point au = lnlncross_pt(Point(0.0, 0.0), cur, *u, *v);
- Point bu = lnlncross_pt(Point(0.0, 0.0), cur, *b.u, *b.v);
- return au.diso() < bu.diso();
- }
- }item[MAXN];
- bool flag[MAXN];
- set<Item> Scan;
- bool cmp(const Item &a, const Item &b) { //極角排序 從-PI到-PI內
- return atan2(a.u->y, a.u->x) < atan2(b.u->y, b.u->x);
- }
- void inputps(int n) {
- Point src, a, b;
- src.input();
- for(int i = 0; i < n; ++i) {
- // 讀取線段並求得相對於光源的座標
- a.input(); a = a - src;
- b.input(); b = b - src;
- // 保證線段的極角序
- if(b < a) swap(a, b);
- ps[LS0(i)] = a;
- ps[LS1(i)] = b;
- item[LS0(i)] = Item(&ps[LS0(i)], &ps[LS1(i)], 0, i);
- item[LS1(i)] = Item(&ps[LS1(i)], &ps[LS0(i)], 1, i);
- }
- sort(item, item + 2 * n, cmp);
- }
- bool sgcross_with_ax(Item &a) { //與射線相交判斷 good 以前不知道的東西
- Point tmp(-1.0, 0.0);
- return (*a.u ^ *a.v) * (*a.u ^ tmp) > 0.0
- && (*a.u ^ tmp) * (tmp ^ *a.v) > 0.0;
- }
- int main() {
- int n;
- while(scanf("%d", &n) != EOF) {
- inputps(n);
- memset(flag,0,sizeof(flag));
- // 初始化極角掃描器 初始射線向量爲(-1.0,0)
- Scan.clear();
- for(int i = 0; i < 2 * n; ++i) {
- cur = *item[i].u;
- if(item[i].type == 1 && sgcross_with_ax(item[i]))
- Scan.insert(item[i]);
- }
- // 極角掃描
- for(int i = 0; i < 2 * n; ++i) {
- cur = *item[i].u;
- if(item[i].type == 1)
- Scan.insert(item[i]);
- else
- Scan.erase(Item(item[i].v, item[i].u, 1, item[i].sgid));
- if(!Scan.empty())
- flag[Scan.begin()->sgid] = true;
- }
- int ans = 0;
- for(int i = 0; i < n; ++i)
- if(flag[i])ans ++;
- printf("%d\n", ans);
- }
- return 0;
- }