半平面交

1. Luogu P4196 [CQOI2006]凸多边形 /【模板】半平面交

题意:逆时针给出 n 个凸多边形的顶点坐标,求它们交的面积

思路:

  1. 先求半平面交的边界线
  2. 再求由边界线构成的凸多边形的面积

时间:nm*log(nm)=500*log500

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 510;
 5 const double eps = 1e-12;
 6 int n, m, cnt;
 7 struct point {
 8   double x, y;
 9 } p[N];
10 struct Line {
11   point s, e;
12 } a[N], q[N];
13 
14 point operator+(point a, point b) {  //向量+
15   return {a.x + b.x, a.y + b.y};
16 }
17 point operator-(point a, point b) {  //向量-
18   return {a.x - b.x, a.y - b.y};
19 }
20 point operator*(point a, double t) {  //数乘
21   return {a.x * t, a.y * t};
22 }
23 double operator*(point a, point b) {  //叉积
24   return a.x * b.y - a.y * b.x;
25 }
26 double angle(Line a) {  //极角(-Pi,Pi]
27   return atan2(a.e.y - a.s.y, a.e.x - a.s.x);
28 }
29 bool cmp(Line a, Line b) {  //按极角+左侧排序
30   double A = angle(a), B = angle(b);
31   return fabs(A - B) > eps ? A < B : (a.e - a.s) * (b.e - a.s) < 0;
32 }
33 point cross(Line a, Line b) {  //直线交点
34   point u = a.s - b.s, v = a.e - a.s, w = b.e - b.s;
35   double t = u * w / (w * v);
36   return a.s + v * t;
37 }
38 bool right(Line a, Line b, Line c) {  //交点在直线右侧
39   point p = cross(b, c);
40   return (a.e - a.s) * (p - a.s) < 0;
41 }
42 double half_plane() {  //半平面交
43   sort(a + 1, a + cnt + 1, cmp);
44   int h = 1, t = 1;
45   q[1] = a[1];
46   for (int i = 2; i <= cnt; i++) {  //枚举直线
47     if (angle(a[i]) - angle(a[i - 1]) < eps) continue;
48     while (h < t && right(a[i], q[t], q[t - 1])) t--;
49     while (h < t && right(a[i], q[h], q[h + 1])) h++;
50     q[++t] = a[i];
51   }
52   while (h < t && right(q[h], q[t], q[t - 1])) t--;
53 
54   q[++t] = q[h];  //封口
55   int k = 0;
56   double res = 0;
57   for (int i = h; i < t; i++) p[++k] = cross(q[i], q[i + 1]);
58   for (int i = 2; i < k; i++) res += (p[i] - p[1]) * (p[i + 1] - p[1]);
59   return res / 2;  //面积
60 }
61 int main() {
62   scanf("%d", &n);
63   while (n--) {
64     scanf("%d", &m);
65     for (int i = 1; i <= m; i++) scanf("%lf%lf", &p[i].x, &p[i].y);
66     for (int i = 1; i <= m; i++) a[++cnt] = {p[i], p[i % m + 1]};
67   }
68   printf("%.3lf\n", half_plane());
69   return 0;
70 }
复制代码

 

2. Luogu P3194 [HNOI2008]水平可见直线

题意:给定 n 条直线 y=Ax+B,从上向下看,输出能看见哪些直线

思路:

  1. 从上向下看,能看见的一定是半平面交的边界线
  2. 让所有直线指向偏右方向,(0,B) 和 (1,A+B) 必过直线 y=Ax+B
  3. 交点在直线上不符合题意,要删除
  4. 本题第1条边一定不会被删除,单调队列已退化为单调栈。也不存在多余的尾巴。

时间:nlogn

复制代码
 1 #include <bits/stdc++.h>  //�ص������㰮�ҵĶ���
 2 using namespace std;
 3 #define endl '\n'
 4 #define ll long long
 5 #define ld long double
 6 
 7 #define x first
 8 #define y second
 9 #define pb push_back
10 #define all(v) v.begin(), v.end()
11 #define cy cout << "YES" << endl
12 #define cn cout << "NO" << endl
13 #define c1 cout << -1 << endl
14 int _, n, k, m;
15 typedef pair<int, int> PII;
16 priority_queue<int> maxx;
17 priority_queue<int, vector<int>, greater<int> > minx;
18 const int N = 2e5 + 10, inf = 1e9;
19 const double eps = 1e-12;
20 const double pi = acos(-1);
21 const int mod = 1e9 + 7;
22 
23 int ans[N];
24 struct point {
25   double x, y;
26 };
27 
28 struct Line {
29   point s, e;
30   int id;
31 } a[N], q[N];
32 
33 point operator+(point a, point b) {  //向量+
34   return {a.x + b.x, a.y + b.y};
35 }
36 point operator-(point a, point b) {  //向量-
37   return {a.x - b.x, a.y - b.y};
38 }
39 point operator*(point a, double t) {  //数乘
40   return {a.x * t, a.y * t};
41 }
42 double operator*(point a, point b) {  //叉积
43   return a.x * b.y - a.y * b.x;
44 }
45 double angle(Line a) {  //极角(-Pi,Pi]
46   return atan2(a.e.y - a.s.y, a.e.x - a.s.x);
47 }
48 bool cmp(Line a, Line b) {  //按极角+左侧排序
49   double A = angle(a), B = angle(b);
50   return fabs(A - B) > eps ? A < B : (a.e - a.s) * (b.e - a.s) < 0;
51 }
52 point cross(Line a, Line b) {  //直线交点
53   point u = a.s - b.s, v = a.e - a.s, w = b.e - b.s;
54   double t = u * w / (w * v);
55   return a.s + v * t;
56 }
57 
58 bool right(Line a, Line b, Line c) {
59   point p = cross(b, c);
60   return (a.e - a.s) * (p - a.s) <= 0;
61 }
62 
63 void half_plane() {
64   sort(a + 1, a + 1 + n, cmp);
65   int h = 1, t = 1;
66   q[1] = a[1];
67   for (int i = 2; i <= n; i++) {  //枚举直线
68     if (angle(a[i]) - angle(a[i - 1]) <= eps) continue;
69     while (h < t && right(a[i], q[t], q[t - 1])) t--;
70     while (h < t && right(a[i], q[h], q[h + 1])) h++;
71     q[++t] = a[i];
72   }
73   int k = 0;
74   for (int i = h; i <= t; i++) ans[k++] = q[i].id;
75   sort(ans, ans + k);
76   for (int i = 0; i < k; i++) cout << ans[i] << " ";
77 }
78 
79 void solve() {
80   cin >> n;
81   for (int i = 1; i <= n; i++) {
82     double A, B;
83     cin >> A >> B;
84     a[i] = {{0, B}, {1, A + B}, i};
85   }
86   half_plane();
87 }
88 
89 int main() {
90   // cin >> _;
91   _ = 1;
92   while (_--) {
93     solve();
94   }
95   return 0;
96 }
复制代码
复制代码
 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 const int N=50005;
 8 const double eps=1e-12;
 9 struct Point{ //点类
10   double x,y;
11   Point(){}
12   Point(double a,double b){x=a;y=b;}
13   Point operator+(Point b){return Point(x+b.x,y+b.y);}
14   Point operator-(Point b){return Point(x-b.x,y-b.y);}
15   Point operator*(double b){return Point(x*b,y*b);}
16   double operator*(Point b){return x*b.y-y*b.x;}
17 };
18 struct Line{ //直线类
19   Point s,e; double ang; int id;
20   Line(){}
21   Line(Point a,Point b,int c){s=a,e=b;ang=atan2((b-a).y,(b-a).x);id=c;}
22   bool operator<(Line& b){ //按极角+左侧排序
23     return fabs(ang-b.ang)>eps ? ang<b.ang : (e-s)*(b.e-s)<0;
24   }
25   Point cross(Line& b){ //直线交点
26     Point u=s-b.s, v=e-s, w=b.e-b.s;
27     double t=u*w/(w*v);
28     return s+v*t;
29   }
30   bool right(Line& b,Line& c){
31     Point p=b.cross(c);
32     return (e-s)*(p-s)<=0; //交点在直线上或右侧
33   }
34 }a[N],q[N];
35 int n,ans[N];
36 
37 double half_plane(){ //半平面交
38   sort(a+1,a+n+1);
39   int h=1, t=1; q[1]=a[1];
40   for(int i=2; i<=n; i++){ //枚举直线
41     if(a[i].ang-a[i-1].ang<eps) continue;
42     while(h<t && a[i].right(q[t],q[t-1]))t--;
43     while(h<t && a[i].right(q[h],q[h+1]))h++;
44     q[++t]=a[i];
45   }
46 
47   int k=0;
48   for(int i=h;i<=t;i++) ans[k++]=q[i].id;
49   sort(ans,ans+k);
50   for(int i=0;i<k;i++) printf("%d ",ans[i]);
51 }
52 int main(){
53   scanf("%d",&n);
54   for(int i=1;i<=n;i++){
55     double A,B;
56     scanf("%lf%lf",&A,&B);
57     a[i]={{0,B},{1,A+B},i}; //指向偏右方
58   }
59   half_plane();
60   return 0;
61 }
复制代码

 

3. Luogu P3256 [JLOI2013]赛车

题意:长直赛道上有 n 辆赛车,给出它们的起始位置 ki 和速度 vi,问哪些赛车曾经处于领跑位置

思路:

  1. 以时间 t 为横轴,位置 s 为纵轴,画直线 s=v*t+k,将是一些斜向右上方的直线
  2. 半平面交的边界线即答案
  3. 用 map 保存具有相同 (v,k) 的赛车
  4. 交点在直线上符合题意,所以不能删除
  5. 半平面交的区域一定在 y 轴右侧,所以补上负 y 轴
  6. 本题第1条边一定不会被删除,单调队列已退化为单调栈。也不存在多余的尾巴。

时间:nlogn

复制代码
 1 #include <bits/stdc++.h>  //�ص������㰮�ҵĶ���
 2 using namespace std;
 3 #define endl '\n'
 4 #define ll long long
 5 #define ld long double
 6 #define double long double
 7 #define x first
 8 #define y second
 9 #define pb push_back
10 #define all(v) v.begin(), v.end()
11 #define cy cout << "YES" << endl
12 #define cn cout << "NO" << endl
13 #define c1 cout << -1 << endl
14 typedef pair<int, int> PII;
15 priority_queue<int> maxx;
16 priority_queue<int, vector<int>, greater<int>> minx;
17 const int N = 10010, inf = 1e9;
18 const double eps = 1e-12;
19 const double pi = acos(-1);
20 const int mod = 1e9 + 7;
21 
22 struct Point {
23   double x, y;
24 };
25 struct Line {
26   Point s, e;
27   vector<int> id;  //这条直线对应的所有赛车
28 } a[N], q[N];
29 int n, cnt, k[N], v[N], ans[N];
30 map<pair<int, int>, vector<int>> mp;
31 
32 Point operator+(Point a, Point b) {  //向量+
33   return {a.x + b.x, a.y + b.y};
34 }
35 Point operator-(Point a, Point b) {  //向量-
36   return {a.x - b.x, a.y - b.y};
37 }
38 Point operator*(Point a, double t) {  //数乘
39   return {a.x * t, a.y * t};
40 }
41 double operator*(Point a, Point b) {  //叉积
42   return a.x * b.y - a.y * b.x;
43 }
44 double angle(Line& a) {  //极角(-Pi,Pi]
45   return atan2(a.e.y - a.s.y, a.e.x - a.s.x);
46 }
47 bool cmp(Line& a, Line& b) {  //按极角+左侧排序
48   double A = angle(a), B = angle(b);
49   return fabs(A - B) > eps ? A < B : (a.e - a.s) * (b.e - a.s) < 0;
50 }
51 Point cross(Line& a, Line& b) {  //直线交点
52   Point u = a.s - b.s, v = a.e - a.s, w = b.e - b.s;
53   double t = u * w / (w * v);
54   return a.s + v * t;
55 }
56 bool right(Line& a, Line& b, Line& c) {  //交点在右侧
57   Point p = cross(b, c);
58   return (a.e - a.s) * (p - a.s) < 0;
59 }
60 void half_plane() {  //半平面交
61   sort(a + 1, a + cnt + 1, cmp);
62   int h = 1, t = 1;
63   q[1] = a[1];
64   for (int i = 2; i <= cnt; i++) {  //枚举直线
65     if (angle(a[i]) - angle(a[i - 1]) < eps) continue;
66     while (h < t && right(a[i], q[t], q[t - 1])) t--;
67     // while(h<t && right(a[i],q[h],q[h+1]))h++;
68     q[++t] = a[i];
69   }
70   int k = 0;
71   for (int i = h; i <= t; i++)
72     for (int id : q[i].id) ans[++k] = id;
73   sort(ans + 1, ans + k + 1);
74   printf("%d\n", k);
75   for (int i = 1; i <= k; i++) printf("%d ", ans[i]);
76 }
77 int main() {
78   scanf("%d", &n);
79   for (int i = 1; i <= n; i++) scanf("%d", &k[i]);
80   for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
81   for (int i = 1; i <= n; i++) mp[{v[i], k[i]}].push_back(i);
82   a[++cnt] = {{0, 1}, {0, 0}};  //补负y轴
83   for (auto& [b, c] : mp)       //{(0,k),(1,v+k),i}
84     a[++cnt] = {{0, b.second}, {1, b.first + b.second}, c};
85   half_plane();
86 }
复制代码

 

练习

Luogu P3222 [HNOI2012]射箭

Luogu P2600 [ZJOI2008]瞭望塔

Luogu P4250 [SCOI2015]小凸想跑步

Luogu P3297 [SDOI2013] 逃考

posted @   rw156  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示