半平面交
1. Luogu P4196 [CQOI2006]凸多边形 /【模板】半平面交
题意:逆时针给出 n 个凸多边形的顶点坐标,求它们交的面积
思路:
- 先求半平面交的边界线
- 再求由边界线构成的凸多边形的面积
时间: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,从上向下看,输出能看见哪些直线
思路:
- 从上向下看,能看见的一定是半平面交的边界线
- 让所有直线指向偏右方向,(0,B) 和 (1,A+B) 必过直线 y=Ax+B
- 交点在直线上不符合题意,要删除
- 本题第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 }
题意:长直赛道上有 n 辆赛车,给出它们的起始位置 ki 和速度 vi,问哪些赛车曾经处于领跑位置
思路:
- 以时间 t 为横轴,位置 s 为纵轴,画直线 s=v*t+k,将是一些斜向右上方的直线
- 半平面交的边界线即答案
- 用 map 保存具有相同 (v,k) 的赛车
- 交点在直线上符合题意,所以不能删除
- 半平面交的区域一定在 y 轴右侧,所以补上负 y 轴
- 本题第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 }
练习