凸包
POJ 1113 Wall(凸包面积问题)
题意: 给定 n n n 个点,连成一座城堡,要求建一座围墙,所有围墙距离城堡最少 L L L 。求围墙最短是多长
思路: 先对所有点求一遍凸包,算出凸包的长度,再加上所有凸包顶点的圆弧合并的圆周长即可。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e3 + 10;
const double eps = 1e-7;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}P[N], ch[N];
struct Line{
Point s, e;
int id;
Line() {}
Line(Point _s, Point _e) {
s = _s; e = _e;
}
void output() {
printf("%d:\n", id);
s.output(); e.output();
}
void adjust() {
if (s.y > e.y) swap(s, e);
}
double length() {
return s.dis(e);
}
bool parallel(Line v) {
return dcmp((e - s) ^ (v.e - v.s)) == 0;
}
Point lineprog(Point p) {
return s + ( ((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
}
double disPointToLine(Point p) {
return fabs((p - s) ^ (e - s) / length());
}
double disPointToSeg(Point p) { //点到线段的距离
if (dcmp((p - s) * (e - s)) < 0 || dcmp((p - e) * (s - e)) < 0)
return min(p.dis(s), p.dis(e));
return disPointToLine(p);
}
int segcrossseg(Line v) {
int d1 = dcmp((e - s) ^ (v.s - s));
int d2 = dcmp((e - s) ^ (v.e - s));
int d3 = dcmp((v.e - v.s) ^ (s - v.s));
int d4 = dcmp((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && dcmp((v.s - s) * (v.s - e)) <= 0) ||
(d2 == 0 && dcmp((v.e - s) * (v.e - e)) <= 0) ||
(d3 == 0 && dcmp((s - v.s) * (s - v.e)) <= 0) ||
(d4 == 0 && dcmp((e - v.s) * (e - v.e)) <= 0);
}
Point crossPoint(Line v) {
double a1 = (v.e - v.s) ^ (s - v.s);
double a2 = (v.e - v.s) ^ (e - v.s);
return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
}
double dissegtoseg(Line v) {
return min(min(disPointToSeg(v.s), disPointToSeg(v.e)),
min(v.disPointToSeg(s), v.disPointToSeg(e)));
}
};
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
double getC(Point p[], int n) {
double res = 0;
for (int i = 1; i <= n; i++) {
double tmp = p[i].dis(p[i - 1]);
// cout << tmp << "\n";
res += tmp;
}
return res;
}
int main() {
int n, L;
while (cin >> n >> L) {
for (int i = 1; i <= n; i++) {
P[i].input();
}
int top = andrew(P, ch, n);
ch[0] = ch[top];
double ans = getC(ch, top);
ans += 2.0 * pi * L;
cout << round(ans) << "\n";
}
return 0;
}
luogu P3829 信用卡凸包(凸包+旋转)
题意: 给定 n n n 张矩形的信用卡,大小一样,但是四个顶点是 1 4 \frac{1}{4} 41 圆弧段。求形成的凸包周长。
思路: 这题和 P O J 1113 POJ\ 1113 POJ 1113 很像,只要反过来思考就行,先找到四个圆弧段的圆心,再进行相应的旋转。得到的点求一次凸包,算出凸包的长度,再加上所有圆弧拼成的一个圆的周长即可。
这里关于 % . 2 f \%.2f %.2f 的输出好像是直接四舍五入的。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-7;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}P[N], ch[N];
struct Line{
Point s, e;
int id;
Line() {}
Line(Point _s, Point _e) {
s = _s; e = _e;
}
void output() {
printf("%d:\n", id);
s.output(); e.output();
}
void adjust() {
if (s.y > e.y) swap(s, e);
}
double length() {
return s.dis(e);
}
bool parallel(Line v) {
return dcmp((e - s) ^ (v.e - v.s)) == 0;
}
Point lineprog(Point p) {
return s + ( ((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
}
double disPointToLine(Point p) {
return fabs((p - s) ^ (e - s) / length());
}
double disPointToSeg(Point p) { //µãµ½Ï߶εľàÀë
if (dcmp((p - s) * (e - s)) < 0 || dcmp((p - e) * (s - e)) < 0)
return min(p.dis(s), p.dis(e));
return disPointToLine(p);
}
int segcrossseg(Line v) {
int d1 = dcmp((e - s) ^ (v.s - s));
int d2 = dcmp((e - s) ^ (v.e - s));
int d3 = dcmp((v.e - v.s) ^ (s - v.s));
int d4 = dcmp((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && dcmp((v.s - s) * (v.s - e)) <= 0) ||
(d2 == 0 && dcmp((v.e - s) * (v.e - e)) <= 0) ||
(d3 == 0 && dcmp((s - v.s) * (s - v.e)) <= 0) ||
(d4 == 0 && dcmp((e - v.s) * (e - v.e)) <= 0);
}
Point crossPoint(Line v) {
double a1 = (v.e - v.s) ^ (s - v.s);
double a2 = (v.e - v.s) ^ (e - v.s);
return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
}
double dissegtoseg(Line v) {
return min(min(disPointToSeg(v.s), disPointToSeg(v.e)),
min(v.disPointToSeg(s), v.disPointToSeg(e)));
}
};
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
double getC(Point p[], int n) {
double res = 0;
for (int i = 1; i <= n; i++) {
double tmp = p[i].dis(p[i - 1]);
// cout << tmp << "\n";
res += tmp;
}
return res;
}
int main() {
int n;
double a, b, r;
scanf("%d", &n);
scanf("%lf%lf%lf", &a, &b, &r);
a -= 2 * r;
b -= 2 * r;
int cnt = 0;
for (int i = 0; i < n; i++) {
vector<Point> now;
Point o;
double angle;
o.input(); scanf("%lf", &angle);
now.push_back(Point(o.x + b / 2.0, o.y + a / 2.0));
now.push_back(Point(o.x + b / 2.0, o.y - a / 2.0));
now.push_back(Point(o.x - b / 2.0, o.y + a / 2.0));
now.push_back(Point(o.x - b / 2.0, o.y - a / 2.0));
for (int i = 0; i < 4; i++) {
P[++cnt] = now[i].Ratate(o, angle);
}
}
int top = andrew(P, ch, cnt);
ch[0] = ch[top];
double ans = getC(ch, top);
ans += 2 * pi * r;
// ans = (ans * 100 + 0.5) / 100.0;
printf("%.2f\n", ans);
// cout << fixed << setprecision(2) << ans << "\n";
return 0;
}
POJ 1228 Grandpa’s Estate(稳定凸包)
题意: 给你若干个点,判断是否可以再加一个点,形成一个新的凸包,并且这个新凸包包含了之前凸包的所有点。
思路: 就是判断这个凸包是否稳定,要使得不能添加一个点使得形成的凸包含所有的点,那么原凸包上的每一条边上至少要有 3 3 3个点(包括端点)。
但是这道题目要注意一下,给你的若干个点,可能是在同一条直线上的。。。,这时候是不合法的。因为这个WA了两发
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-7;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}P[N], ch[N];
struct Line{
Point s, e;
int id;
Line() {}
Line(Point _s, Point _e) {
s = _s; e = _e;
}
void output() {
printf("%d:\n", id);
s.output(); e.output();
}
void adjust() {
if (s.y > e.y) swap(s, e);
}
double length() {
return s.dis(e);
}
bool parallel(Line v) {
return dcmp((e - s) ^ (v.e - v.s)) == 0;
}
Point lineprog(Point p) {
return s + ( ((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
}
double disPointToLine(Point p) {
return fabs((p - s) ^ (e - s) / length());
}
double disPointToSeg(Point p) { //µãµ½Ï߶εľàÀë
if (dcmp((p - s) * (e - s)) < 0 || dcmp((p - e) * (s - e)) < 0)
return min(p.dis(s), p.dis(e));
return disPointToLine(p);
}
int segcrossseg(Line v) {
int d1 = dcmp((e - s) ^ (v.s - s));
int d2 = dcmp((e - s) ^ (v.e - s));
int d3 = dcmp((v.e - v.s) ^ (s - v.s));
int d4 = dcmp((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && dcmp((v.s - s) * (v.s - e)) <= 0) ||
(d2 == 0 && dcmp((v.e - s) * (v.e - e)) <= 0) ||
(d3 == 0 && dcmp((s - v.s) * (s - v.e)) <= 0) ||
(d4 == 0 && dcmp((e - v.s) * (e - v.e)) <= 0);
}
Point crossPoint(Line v) {
double a1 = (v.e - v.s) ^ (s - v.s);
double a2 = (v.e - v.s) ^ (e - v.s);
return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
}
double dissegtoseg(Line v) {
return min(min(disPointToSeg(v.s), disPointToSeg(v.e)),
min(v.disPointToSeg(s), v.disPointToSeg(e)));
}
};
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
bool judge(Point a, Point b, int n) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
double tmp = (a - P[i]) ^ (b - P[i]);
if (dcmp(tmp) == 0) cnt++;
}
return cnt >= 3;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
P[i].input();
}
int top = andrew(P, ch, n);
if (top < 3) {
printf("NO\n");
continue;
}
ch[0] = ch[top];
bool f = true;
for (int i = 1; i <= top && f; i++) {
if (!judge(ch[i], ch[i - 1], n)) {
f = false;
}
}
if (f) printf("YES\n");
else printf("NO\n");
}
return 0;
}
luogu P3194 水平可见直线(单调栈维护下凸壳,一般式的直线)
题意: 给定 n n n 条直线,若从 y y y 轴的正无穷处观察,求能看到哪些直线。
思路: 先按斜率为第一关键字从小到大,截距为第二关键字从大到小排序,然后根据直线交点的左右关系来维护单调栈,最后求出一个下凸壳,然后栈中的直线就是能看到的所有直线了。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e5 + 10;
struct node{
int a, b, id;
}li[N];
bool cmp(node x, node y) {
if (x.a == y.a) return x.b > y.b;
return x.a < y.a;
}
double calx(int i, int j) {
return ((1.0 * (li[i].b - li[j].b)) / (li[j].a - li[i].a));
}
int st[N];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &li[i].a, &li[i].b);
li[i].id = i;
}
sort(li + 1, li + 1 + n, cmp);
int top = 0;
for (int i = 1; i <= n; i++) {
if (i > 1 && li[i].a == li[i - 1].a) continue;
while (top > 1 && calx(st[top], i) <= calx(st[top], st[top - 1]))
top--;
st[++top] = i;
}
vector<int> ans;
for (int i = 1; i <= top; i++) {
ans.push_back(li[st[i]].id);
}
sort(ans.begin(), ans.end());
for (int i = 0; i < (int)ans.size(); i++)
printf("%d ", ans[i]);
return 0;
}
POJ 1873 The Fortified Forest(凸包+状压枚举)
题意: 给定 n n n 棵树,每棵树对应二维坐标、价值和砍掉后能变成的绳子长度。现要求将砍掉一部分树,做成绳子,并能将剩下的树全部围起来。要求砍掉的树的价值最小。
思路: 因为题目给定
n
<
=
15
n <= 15
n<=15 很小,直接枚举所有状态,若消耗的价值更小就更新答案即可。做的时候将只剩下一个点的情况拿出来特判了,其实并不需要,因为只剩一个点的时候,正常算凸包周长,算出来的也是0。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-7;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
int id;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}P[N], ch[N], q[N];
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
int main() {
int n;
int kase = 0;
vector<int> cc(35000, 0);
for (int i = 0; i < 35000; i++) {
for (int j = 0; j < 15; j++) {
if ((i >> j) & 1) continue;
else cc[i]++;
}
}
while (scanf("%d", &n)) {
if (n == 0) break;
vector<double> l(n), v(n);
for (int i = 0; i < n; i++) {
double x, y;
double len, val;
scanf("%lf%lf%lf%lf", &x, &y, &v[i], &l[i]);
P[i] = Point(x, y);
P[i].id = i;
}
int res = -1;
double cost = 1e9;
double last_len = 0;
for (int s = 1; s < (1 << n); s++) {
int cnt = 0;
double alen = 0;
double acost = 0;
for (int i = 0; i < n; i++) {
if ((s >> i) & 1) q[++cnt] = P[i];
else {
alen += l[i];
acost += v[i];
}
}
// if (cnt == 0) continue;
int top = andrew(q, ch, cnt);
// printf("----\n");
// for (int i = 1; i <= top; i++) {
// ch[i].output();
// }
// printf("----\n");
// if (top == 1) {
// if (dcmp(acost - cost) < 0) {
// res = s;
// last_len = alen;
// cost = acost;
// }
// else if (dcmp(acost - cost) == 0 && cc[s] < cc[res]) {
// res = s;
// last_len = alen;
// }
// }
// else {
double Ctr = 0;
ch[0] = ch[top];
for (int i = 1; i <= top; i++) {
Ctr += ch[i].dis(ch[i - 1]);
}
if (dcmp(alen - Ctr) >= 0) {
if (dcmp(acost - cost) < 0) {
res = s;
last_len = alen - Ctr;
cost = acost;
}
else if (dcmp(acost - cost) == 0 && cc[res] > cc[s]) {
res = s;
last_len = alen - Ctr;
}
}
// }
// for (int i = n - 1; i >= 0; i--) {
// if ((res >> i) & 1) printf("1");
// else printf("0");
// }
// printf("\n");
// printf("%.2f\n***********\n", last_len);
}
vector<int> ans;
for (int i = 0; i < n; i++) {
if ((~res >> i) & 1) ans.push_back(i + 1);
}
printf("Forest %d\n", ++kase);
printf("Cut these trees:");
for (int i = 0; i < (int)ans.size(); i++) {
printf(" %d", ans[i]);
}
printf("\n");
printf("Extra wood: %.2f\n", last_len);
printf("\n");
}
return 0;
}
旋转卡壳
POJ 2187 Beauty Contest (凸包最远点对)
题意: 给你若干个点,求凸包直径(也就是最远点对)
思路: 模板题,利用旋转卡壳求一下即可,主要是验一下板子
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-7;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
int id;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}P[N], ch[N];
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
double solve(Point p[], int n) {
double maxd = -1;
int j = 2;
for (int i = 0; i < n; i++) {
while (((p[i] - p[i + 1]) ^ (p[i] - p[j])) < ((p[i] - p[i + 1]) ^ (p[i] - p[(j + 1) % n])))
j = (j + 1) % n;
double tmp = max((p[i] - p[j]) * (p[i] - p[j]), (p[i] - p[i + 1]) * (p[i] - p[i + 1]));
if (tmp > maxd) maxd = tmp;
}
return maxd;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
P[i].input();
}
int top = andrew(P, ch, n);
ch[0] = ch[top];
double ans = solve(ch, top);
printf("%.0f\n", ans);
return 0;
}
POJ 3608 Bridge Across Islands(两个凸包最近距离)
题意: 给定两个凸包,求两凸包的最近距离
思路: 先找到凸包 P P P 中的点中纵坐标最小的点 p m i n pmin pmin ,找到凸包 Q Q Q 中的点中纵坐标最大的点 q m a x qmax qmax 。然后以线段 L i n e ( p m i n , p m i n + 1 ) Line(pmin, pmin+1) Line(pmin,pmin+1) 为底从 Q Q Q 凸包中 q m a x qmax qmax 开始逆时针旋转找到面积最大的三角形的顶点。然后判断线段 L i n e ( p m i n , p m i n + 1 ) Line(pmin,pmin+1) Line(pmin,pmin+1) 与线段 L i n e ( q m a x , q m a x + 1 ) Line(qmax, qmax+1) Line(qmax,qmax+1) 之间的最小距离。然后 p m i n pmin pmin 点也逆时针旋转,要求遍历到凸包上的所有边,因此要循环 n n n 遍。
前提是凸包点要先按照逆时针排序排好,一开始以坐标原点为基准点进行极角排序,当做了逆时针排序,但是若原点不在凸包内部,这样排序就不是逆时针排序了。 这里我直接将给定的点求一次凸包,这样直接是逆时针的了,其实还可以随便在凸包上选取一点,按该点进行极角排序,结果就是逆时针的了。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-7;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}p[N], q[N], np[N], nq[N];
struct Line{
Point s, e;
int id;
Line() {}
Line(Point _s, Point _e) {
s = _s; e = _e;
}
void output() {
printf("%d:\n", id);
s.output(); e.output();
}
void adjust() {
if (s.y > e.y) swap(s, e);
}
double length() {
return s.dis(e);
}
bool parallel(Line v) {
return dcmp((e - s) ^ (v.e - v.s)) == 0;
}
Point lineprog(Point p) {
return s + ( ((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
}
double disPointToLine(Point p) {
return fabs((p - s) ^ (e - s) / length());
}
double disPointToSeg(Point p) { //点到线段的距离
if (dcmp((p - s) * (e - s)) < 0 || dcmp((p - e) * (s - e)) < 0)
return min(p.dis(s), p.dis(e));
return disPointToLine(p);
}
double dissegtoseg(Line v) {
return min(min(disPointToSeg(v.s), disPointToSeg(v.e)),
min(v.disPointToSeg(s), v.disPointToSeg(e)));
}
};
double cross(Point a, Point b, Point c) {
return (b - a) ^ (c - a);
}
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
int main() {
int n, m;
while (scanf("%d%d", &n, &m)) {
if (n + m == 0) break;
for (int i = 1; i <= n; i++)
np[i].input();
for (int i = 1; i <= m; i++)
nq[i].input();
n = andrew(np, p, n);
m = andrew(nq, q, m);
// cout << n << " " << m << "\n";
p[0] = p[n];
q[0] = q[m];
int pmin = 0, qmax = 0;
for (int i = 0; i < n; i++) {
if (p[i].y < p[pmin].y) pmin = i;
}
for (int i = 0; i < m; i++) {
if (q[i].y > q[qmax].y) qmax = i;
}
double tmp, ans = 2e9;
// cout << ans << "\n";
for (int i = 0; i < n; i++) {
while (dcmp(cross(p[pmin], p[pmin + 1], q[qmax + 1]) - cross(p[pmin], p[pmin + 1], q[qmax])) > 0)
qmax = (qmax + 1) % m;
Line pp = Line(p[pmin], p[pmin + 1]);
Line qq = Line(q[qmax], q[qmax + 1]);
ans = min(ans, pp.dissegtoseg(qq));
pmin = (pmin + 1) % n;
}
printf("%.5f\n", ans);
}
return 0;
}
luogu P3187 最小矩形覆盖(凸包+旋转卡壳)(奇怪的 -0 问题)
题意: 给定若干个点,求能覆盖所有点的最小矩形面积,再按逆时针输出四个坐标(纵坐标最小的第一个输出,若纵坐标相同,选横坐标更小的)。
思路:
感性猜测,最小矩形至少有一边与凸包边重合,旋转卡壳维护三个点
最上方:用叉积求面积最大
最左方:用点积求最小
最右方:用点积求最大
四个点用平行线加投影点求出后按逆时针存入。
不知道为什么这个题的精度为什么这么恶心,我把数据改成0,还是会输出 − 0.00000 -0.00000 −0.00000,还得要我手动输出 0.00000 0.00000 0.00000 才能过。。。。
又交了几发试了一下,发现先对所有数据判eps置
0
0
0就不行,在输出前判eps就行??????
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 5e5 + 10;
const double eps = 1e-8;
const double pi = acos(-1.0);
int dcmp(double x) {
if (fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator==(const Point& b) const {
return dcmp(x - b.x) == 0 && dcmp(y - b.y) == 0;
}
bool operator<(const Point& b) const {
return dcmp(x - b.x) == 0 ? dcmp(y - b.y) < 0 : x < b.x;
}
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.x + y * b.y;
}
double operator^(const Point& b) const {
return x * b.y - y * b.x;
}
Point operator/(const double& k) const {
return Point(x / k, y / k);
}
Point operator*(const double& k) const {
return Point(x * k, y * k);
}
double dis(Point p) {
return sqrt((*this - p) * (*this - p));
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
Point Ratate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
}P[N], ch[N];
struct Line{
Point s, e;
int id;
Line() {}
Line(Point _s, Point _e) {
s = _s; e = _e;
}
void output() {
printf("%d:\n", id);
s.output(); e.output();
}
void adjust() {
if (s.y > e.y) swap(s, e);
}
double length() {
return s.dis(e);
}
bool parallel(Line v) {
return dcmp((e - s) ^ (v.e - v.s)) == 0;
}
Point lineprog(Point p) {
return s + ( ((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
}
double disPointToLine(Point p) {
return fabs((p - s) ^ (e - s) / length());
}
double disPointToSeg(Point p) {
if (dcmp((p - s) * (e - s)) < 0 || dcmp((p - e) * (s - e)) < 0)
return min(p.dis(s), p.dis(e));
return disPointToLine(p);
}
int segcrossseg(Line v) {
int d1 = dcmp((e - s) ^ (v.s - s));
int d2 = dcmp((e - s) ^ (v.e - s));
int d3 = dcmp((v.e - v.s) ^ (s - v.s));
int d4 = dcmp((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && dcmp((v.s - s) * (v.s - e)) <= 0) ||
(d2 == 0 && dcmp((v.e - s) * (v.e - e)) <= 0) ||
(d3 == 0 && dcmp((s - v.s) * (s - v.e)) <= 0) ||
(d4 == 0 && dcmp((e - v.s) * (e - v.e)) <= 0);
}
Point crossPoint(Line v) {
double a1 = (v.e - v.s) ^ (s - v.s);
double a2 = (v.e - v.s) ^ (e - v.s);
return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
}
double dissegtoseg(Line v) {
return min(min(disPointToSeg(v.s), disPointToSeg(v.e)),
min(v.disPointToSeg(s), v.disPointToSeg(e)));
}
};
double cross(Point a, Point b, Point c) {
return (b - a) ^ (c - a);
}
double dot(Point a, Point b, Point c) {
return (b - a) * (c - a);
}
int andrew(Point p[], Point ch[], int n) {
sort(p + 1, p + 1 + n);
// n = unique(p + 1, p + 1 + n) - p - 1;
int top = 0;
for (int i = 1; i <= n; i++) {
while ((top > 1) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
int tmp = top;
for (int i = n - 1; i; i--) {
while ((top > tmp) && ( (ch[top] - ch[top - 1]) ^ (p[i] - ch[top]) ) <= 0)
--top;
ch[++top] = p[i];
}
if (n > 1) top--;
return top;
}
vector<Point> ans(4);
double minS;
void solve(Point p[], int n) {
int mid = 1, r = 1, l = 1;
for (int i = 0; i < n; i++) {
while (dcmp(cross(p[i], p[i + 1], p[mid]) - cross(p[i], p[i + 1], p[(mid + 1) % n])) <= 0)
mid = (mid + 1) % n;
while (dcmp(dot(p[i], p[i + 1], p[r]) - dot(p[i], p[i + 1], p[(r + 1) % n])) <= 0)
r = (r + 1) % n;
if (i == 0) l = mid;
while (dcmp(dot(p[i], p[i + 1], p[l]) - dot(p[i], p[i + 1], p[(l + 1) % n])) >= 0)
l = (l + 1) % n;
vector<Point> tmp;
Line dn = Line(p[i], p[i + 1]);
Line up = Line(p[mid], Point(p[mid].x + (p[i + 1].x - p[i].x), p[mid].y + (p[i + 1].y - p[i].y)));
tmp.push_back(dn.lineprog(p[l]));
tmp.push_back(dn.lineprog(p[r]));
tmp.push_back(up.lineprog(p[r]));
tmp.push_back(up.lineprog(p[l]));
// double dd = p[i].dis(p[i + 1]);
// double right = dot(p[i], p[i + 1], p[r]) / dd;
// double left = dot(p[i], p[i + 1], p[l]) / dd;
// double high = cross(p[i], p[i + 1], p[mid]) /dd;
// high = fabs(high);
// double S = (right - left) * high;
double S = tmp[0].dis(tmp[1]) * tmp[1].dis(tmp[2]);
S = fabs(S);
if (dcmp(S - minS) < 0) {
minS = S;
for (int i = 0; i < 4; i++)
ans[i] = tmp[i];
// ans[1] = p[i] + (p[i + 1] - p[i]) * (right / dd);
// ans[0] = p[i] + (p[i + 1] - p[i]) * (left / dd);
// ans[2] = ans[1] + (p[r] - ans[1]) * (high / p[r].dis(ans[1]));
// ans[3] = ans[0] + (p[l] - ans[0]) * (high / p[l].dis(ans[0]));
}
}
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
P[i].input();
}
int top = andrew(P, ch, n);
ch[0] = ch[top];
// for (int i = 0; i < top; i++) {
// ch[i].output();
// }
// if (top == 2) {
// printf("0.00000\n");
// Point p1 = ch[0];
// Point p2 = ch[1];
// if (dcmp(p1.y - p2.y) > 0 || (dcmp(p1.y - p2.y) == 0) && p1.x > p2.x) {
// swap(p1, p2);
// }
// printf("%.5f %.5f\n", p1.x, p1.y);
// printf("%.5f %.5f\n", p2.x, p2.y);
// printf("%.5f %.5f\n", p2.x, p2.y);
// printf("%.5f %.5f\n", p1.x, p1.y);
// return 0;
// }
minS = 1e20;
solve(ch, top);
printf("%.5f\n", minS);
// for (int i = 0; i < 4; i++) {
// if (dcmp(ans[i].x) == 0)
// ans[i].x == 0;
// if (dcmp(ans[i].y) == 0)
// ans[i].y = 0;
// }
int pos = 0;
for (int i = 1; i < 4; i++) {
if (dcmp(ans[i].y - ans[pos].y) < 0 || (dcmp(ans[i].y - ans[pos].y) == 0 && ans[i].x < ans[pos].x))
pos = i;
}
for (int i = pos; i < pos + 4; i++) {
double x = ans[i % 4].x, y = ans[i % 4].y;
if (dcmp(x) == 0) printf("0.00000 ");
else printf("%.5f ", x);
if (dcmp(y) == 0) printf("0.00000\n");
else printf("%.5f\n", y);
// printf("%.5f %.5f\n", ans[i % 4].x, ans[i % 4].y);
// pos = (pos + 1) % 4;
}
return 0;
}
扫描线
luogu P5490 (扫描线求面积并)
题意: 若干个矩形,求面积
思路: 用扫描线,这里的线段树节点意义和平常稍有不同,因为每个节点要表示的是一条线段,而当线段树某一节点的左右节点相等时(叶子节点时)这明显是一个点,因此对每个离散化的点的意义稍作调整,即当前节点 x [ i ] x[i] x[i] 表示 x [ i ] − > x [ i + 1 ] x[i]->x[i+1] x[i]−>x[i+1] 的距离,建线段树时,节点也是 [ 1 , a l l n o d e s − 1 ] [1, allnodes - 1] [1,allnodes−1] 即 [ 1 , a l l n o d e s ) [1, allnodes) [1,allnodes)。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
//#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e6 + 10;
int n;
ll x[N << 1];
struct Line {
ll l, r, h;
int mark;
Line() {}
Line(ll _l, ll _r, ll _h, int m) {
l = _l; r = _r, h = _h; mark = m;
}
bool operator<(const Line& v) const {
return h < v.h;
}
}li[N << 1];
#define ls (rt << 1)
#define rs ((rt << 1) | 1)
struct SegTree{
int l, r, cnt;
ll len; //覆盖长度
}tr[N << 2];
void build(int l, int r, int rt) {
tr[rt].l = l; tr[rt].r = r;
tr[rt].cnt = 0;
tr[rt].len = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls);
build(mid + 1, r, rs);
}
void pushup(int rt) {
int l = tr[rt].l, r = tr[rt].r;
if (tr[rt].cnt) tr[rt].len = x[r + 1] - x[l];
else tr[rt].len = tr[ls].len + tr[rs].len;
}
void modify(ll st, ll ed, int rt, int v) {
int l = tr[rt].l, r = tr[rt].r;
if (x[r + 1] <= st || ed <= x[l])
return;
if (st <= x[l] && x[r + 1] <= ed) {
tr[rt].cnt += v;
pushup(rt);
return;
}
modify(st, ed, ls, v);
modify(st, ed, rs, v);
pushup(rt);
}
int main() {
scanf("%d", &n);
ll xx[2], yy[2];
for (int i = 1; i <= n; i++) {
scanf("%lld%lld%lld%lld", &xx[0], &yy[0], &xx[1], &yy[1]);
x[2 * i - 1] = xx[0]; x[2 * i] = xx[1];
li[2 * i - 1] = Line(xx[0], xx[1], yy[0], 1);
li[2 * i] = Line(xx[0], xx[1], yy[1], -1);
}
n <<= 1;
sort(li + 1, li + 1 + n);
sort(x + 1, x + 1 + n);
int tot = unique(x + 1, x + 1 + n) - x - 1;
build(1, tot - 1, 1); //是tot-1 的原因是x[i]代表的是[x[i], x[i+1])
ll ans = 0;
for (int i = 1; i < n; i++) {
modify(li[i].l, li[i].r, 1, li[i].mark);
ans += tr[1].len * (li[i + 1].h - li[i].h);
}
printf("%lld\n", ans);
return 0;
}
/*
2
100 100 200 200
150 150 250 255
*/
luogu P1856(扫描线求周长并)
题意: 若干个矩形,求整个图形的周长,这里包括了内周长
思路: 和求面积并其实差不多,改动的主要是每条线段的贡献的算法,从一个方向扫过去,每条线段的贡献是没加上该边时覆盖的总长度与加上改变后覆盖的总长度之差的绝对值。
对每条线段的排序要注意一点:对于相同高的线段,要按先加边再删边的顺序排序。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
//#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e6 + 10;
int n;
ll x[N << 1];
ll y[N << 1];
struct node{
ll x, y;
node() {}
node(ll _x, ll _y) {
x = _x; y = _y;
}
}a[N];
struct Line {
ll l, r, h;
int mark;
Line() {}
Line(ll _l, ll _r, ll _h, int m) {
l = _l; r = _r, h = _h; mark = m;
}
bool operator<(const Line& v) const { //对于同一层高的,应先加边再删边
return h < v.h || (h == v.h && mark > v.mark);
}
}li[N << 1];
#define ls (rt << 1)
#define rs ((rt << 1) | 1)
struct SegTree{
int l, r, cnt;
ll len; //覆盖长度
}tr[N << 2];
void build(int l, int r, int rt) {
tr[rt].l = l; tr[rt].r = r;
tr[rt].cnt = 0;
tr[rt].len = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls);
build(mid + 1, r, rs);
}
void pushup(int rt) {
int l = tr[rt].l, r = tr[rt].r;
if (tr[rt].cnt) tr[rt].len = x[r + 1] - x[l];
else tr[rt].len = tr[ls].len + tr[rs].len;
}
void modify(ll st, ll ed, int rt, int v) {
int l = tr[rt].l, r = tr[rt].r;
if (x[r + 1] <= st || ed <= x[l])
return;
if (st <= x[l] && x[r + 1] <= ed) {
tr[rt].cnt += v;
pushup(rt);
return;
}
modify(st, ed, ls, v);
modify(st, ed, rs, v);
pushup(rt);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &a[2 * i - 1].x, &a[2 * i - 1].y);
scanf("%lld%lld", &a[2 * i].x, &a[2 * i].y);
}
for (int i = 1; i <= n; i++) {
node p1 = a[2 * i - 1], p2 = a[2 * i];
x[2 * i - 1] = p1.x; x[2 * i] = p2.x;
li[2 * i -1] = Line(p1.x, p2.x, p1.y, 1);
li[2 * i] = Line(p1.x, p2.x, p2.y, -1);
}
sort(x + 1, x + 1 + 2 * n);
sort(li + 1, li + 1 + 2 * n);
int tot = unique(x + 1, x + 1 + 2 * n) - x - 1;
build(1, tot - 1, 1);
ll ans = 0;
for (int i = 1; i <= 2 * n; i++) {
ll pre = tr[1].len;
modify(li[i].l, li[i].r, 1, li[i].mark);
ll now = tr[1].len;
ans += abs(pre - now);
}
for (int i = 1; i <= n; i++) {
node p1 = a[2 * i - 1], p2 = a[2 * i];
x[2 * i - 1] = p1.y; x[2 * i] = p2.y;
li[2 * i - 1] = Line(p1.y, p2.y, p1.x, 1);
li[2 * i] = Line(p1.y, p2.y, p2.x, -1);
}
sort(x + 1, x + 1 + 2 * n);
sort(li + 1, li + 1 + 2 * n);
tot = unique(x + 1, x + 1 + 2 * n) - x - 1;
build(1, tot - 1, 1);
for (int i = 1; i <= 2 * n; i++) {
ll pre = tr[1].len;
modify(li[i].l, li[i].r, 1, li[i].mark);
ll now = tr[1].len;
ans += abs(pre - now);
}
printf("%lld\n", ans);
return 0;
}
Hdu 3265 Posters(扫描线+矩形分割)
题意: 地图上有若干个矩形,矩形内部有破洞,求在地图上被矩形覆盖的区域面积大小是多少。
思路: 将矩形分割为四个小矩形,然后再用扫描线即可。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
//#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e6 + 10;
int n;
ll x[N << 1];
struct Line {
ll l, r, h;
int mark;
Line() {}
Line(ll _l, ll _r, ll _h, int m) {
l = _l; r = _r, h = _h; mark = m;
}
bool operator<(const Line& v) const {
return h < v.h;
}
}li[N << 1];
#define ls (rt << 1)
#define rs ((rt << 1) | 1)
struct SegTree{
int l, r, cnt;
ll len; //覆盖长度
}tr[N << 2];
void build(int l, int r, int rt) {
tr[rt].l = l; tr[rt].r = r;
tr[rt].cnt = 0;
tr[rt].len = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls);
build(mid + 1, r, rs);
}
void pushup(int rt) {
int l = tr[rt].l, r = tr[rt].r;
if (tr[rt].cnt) tr[rt].len = x[r + 1] - x[l];
else tr[rt].len = tr[ls].len + tr[rs].len;
}
void modify(ll st, ll ed, int rt, int v) {
int l = tr[rt].l, r = tr[rt].r;
if (x[r + 1] <= st || ed <= x[l])
return;
if (st <= x[l] && x[r + 1] <= ed) {
tr[rt].cnt += v;
pushup(rt);
return;
}
modify(st, ed, ls, v);
modify(st, ed, rs, v);
pushup(rt);
}
int main() {
while (scanf("%d", &n)) {
if (n == 0) break;
ll x1, y1, x2, y2, x3, y3, x4, y4;
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &x1, &y1);
scanf("%lld%lld", &x2, &y2);
scanf("%lld%lld", &x3, &y3);
scanf("%lld%lld", &x4, &y4);
x[4 * i - 3] = x1;
x[4 * i - 2] = x2;
x[4 * i - 1] = x3;
x[4 * i] = x4;
li[8 * i - 7] = Line(x1, x3, y1, 1);
li[8 * i - 6] = Line(x3, x4, y1, 1);
li[8 * i - 5] = Line(x4, x2, y1, 1);
li[8 * i - 4] = Line(x3, x4, y3, -1);
li[8 * i - 3] = Line(x3, x4, y4, 1);
li[8 * i - 2] = Line(x3, x4, y2, -1);
li[8 * i - 1] = Line(x1, x3, y2, -1);
li[8 * i] = Line(x4, x2, y2, -1);
}
sort(x + 1, x + 1 + 4 * n);
sort(li + 1, li + 1 + 8 * n);
int tot = unique(x + 1, x + 1 + 4 * n) - x - 1;
build(1, tot - 1, 1);
ll ans = 0;
for (int i = 1; i < 8 * n; i++) {
modify(li[i].l, li[i].r, 1, li[i].mark);
ans += tr[1].len * (li[i + 1].h - li[i].h);
}
printf("%lld\n", ans);
}
return 0;
}