计算 几何

y总的课确实非常细致体贴,整理了一些基础知识,非常重要的还是;

常数和公式
pie = acos(-1) //  π
c*c = a*a + b*b - 2*a*b*cos(C); // 余弦定理
比较函数
const double eps = 1e-8;
int sign(double x) {
	if(fabs(x) < eps) return 0;
	if(x < 0) return -1;
	return 1;
}
int cmp(double x,double y) {
	if(fabs(x - y) < eps) return 0;
	if(x < y) return -1;
	return 1;
}
平面向量
double dot(point a,point b) {
	return a.x * b.x + a.y *b.y;
}
double cross(point a,point b) {
	return a.x *b.y - b.x * a.y;
}
double get_len(point a) {
	return sqrt(dot(a,a));
}
double get_angle(point a,point b) {
	return acos(dot(a,b) / get_len(a) / get_len(b) );
}
double area(point a,point b,point c) {
	return cross(b - a,c - a);
}
point rotate(point a,double angle) {
	return point(a.x * cos(angle) + a.y*sin(angel), -a.x*sin(angle) + a.y * cos(angle));
} 
点和直线
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct point {
	int x,y;
};
point get_line_intersection(point p,vec v,point q,vec w){
	vec u = p - q;
	double t = cross(w, u) / cross(v, w);
	return p + v * t;
}
double distance_to_line(point p,point a,point b) {
	vec v1 = b - a , v2 = p - a;
	return fabs(cross(v1,v2) / get_length(v1));
}
double distance_to_segment(point p,point a,point b) {
	if(a == b) return get_length(p - a);
	vec v1 = b - a, v2 = p - a, v3 = p - b;
	if(sign(dot(v1,v2)) < 0) return get_length(v2);
	if(sign(dot(v1,v3)) > 0) return get_length(v3);
	return distance_to_line(p, a, b);
}
double get_line_projection(point p,point a,point b) {
	vec v = b - a; 
	return a + v * (dot(v, p - a) / dot(v, v));
}
bool on_segment(point p,point a,point b) {
	return sign(cross(p - a,p - b)) == 0 && sign(dot(p - a,p - b)) <= 0; 
}
bool segment_intersection(point a1,point a2,point b1,point b2) {
	double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1,b2 - a1);
	double c3 = cross(b2 - b1, a2 - a1), c4 = cross(b2 - b1,a1 - b1);
	return sign(c1) * sign(c2) <= 0 && sign(c3) * sign(c4) <= 0;
}
     直线定理
        (1) 一般式 ax + by + c = 0
        (2) 点向式 p0 + vt
        (3) 斜截式 y = kx + b
     常用操作
        (1) 判断点在直线上 A x B = 0
        (2) 两直线相交
        // cross(v, w) == 0则两直线平行或者重合
多边形
double polygon_area(point p[],int n) {
	double s = 0;
	for(int i = 1;i + 1 < n; i ++) {
		s += cross(p[i] - p[0], p[i+1] - p[i]);
	}
	return s / 2;
}

AcWing2983

很简单的,就是单纯的判断点和直线的关系的题;
直接使用向量的叉乘来做;
注意到每个点,关于每一条线,都有一个特殊的两段性,那么就直接二分;

#include<iostream>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PLL;
const int N = 5010;
int n ,m;
PLL a[N] ,b[N];
int ans[N];
LL cross(LL x1,LL y1,LL x2,LL y2) {
	return x1 * y2 - x2 * y1;
}
LL area(PLL a,PLL b,PLL c) {
	return cross(b.x - a.x , b.y - a.y, c.x - a.x ,c.y - a.y);
}
int find(LL x,LL y) {
	int l = 0, r = n;
	while(l < r) {
		int mid = l + r >> 1;
		if(area(b[mid], a[mid], {x, y} ) ) r = mid;
		else l = mid + 1;
	}
	return r;
}
int main()
{
	bool is_first = true;
	while(scanf("%d",&n) ,n) {
		LL x1 , y1, x2 , y2;
		scanf("%d%lld%lld%lld%lld",&m,&x1,&y1,&x2,&y2);
		for(int i=0;i < n;i ++) {
			LL u , l;
			scanf("%lld%lld",&u,&l);
			a[i] = {u, y1}, b[i] = {l ,y2};
		}
		a[n] = {x2, y1}, b[n] = {x2, y2};
		if(is_first) is_first = false;
		else puts("");
		memset(ans,0,sizeof ans);
		while(m -- ) {
			LL x ,y;
			scanf("%lld%lld",&x,&y);
			ans[find(x,y)] ++;
		}
		for(int i = 0;i <=n;i++){
			printf("%d: %d\n",i,ans[i]);
		}
	}
}

AcWing2984

不简单的题,需要稍稍进行一些转换才可以做,将投影转化为有交点,然后发现数据范围比较小,可以暴力。
虽然我是绝对想不到可以这么做的,不过我认为,之所以可以这么去想,大概是因为我们应该要学会信息的转化,从题目中所给的信息转化为有已知模型的信息,再用已知模型求解,以上;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double,double> PDD;
const int N = 210;
const double eps = 1e-8;
int n;
PDD q[N], a[N], b[N];
int sign(double x) {
	if(fabs(x) < eps) return 0;
	if(x < 0) return -1;
	return 1;
}
int cmp(double x,double y) {
	if( fabs(x - y) < eps ) return 0;
	if( x < y) return -1;
	return 1;
}
double cross(double x1, double y1, double x2,double y2) {
	return x1 * y2 - x2 * y1;
}
double area(PDD a,PDD b,PDD c){
	return cross(b.x - a.x , b.y - a.y, c.x - a.x ,c.y - a.y);
}
bool check() {
	for(int i=0;i<n*2;i++) 
		for(int j=i+1;j<n*2;j ++) {
			if(!cmp(q[i].x, q[j].x ) && !cmp(q[i].y, q[j].y)) continue;
			bool flag = true;
			for(int k = 0; k < n;k ++) 
				if(sign(area(q[i],q[j],a[k])) * sign(area(q[i],q[j],b[k])) > 0 ){
					flag = false;
					break;
				}
			if(flag) return true;
		}
		return false;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T -- ) {
		scanf("%d",&n);
		for(int i=0,k = 0;i < n;i ++) {
			double x1, y1, x2, y2;
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			q[k++] = {x1, y1}, q[k++] = {x2,y2};
			a[i] = {x1, y1}, b[i] = {x2, y2};
		}
		
		if(check()) puts("Yes!");
		else puts("No!");
	}
	return 0;
}

AcWing1401

凸包,单调栈来求,至于为什么用单调栈来求,我认为,是因为定下来一个端点,要选最大的斜率才行,所以要单调栈,然后至于说为什么要用排序,排了之后就能保证不会出现凹的形状,你拼出来的肯定是凸包而不是凹包,想了很久才想明白这两个点;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double,double> PDD;
const int N = 10010;
int n;
PDD q[N];
int stk[N];
bool used[N];
double get_dist(PDD a,PDD b){
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx * dx + dy *dy);
}
PDD operator - (PDD a,PDD b){
	return {a.x - b.x , a.y - b.y};
}
double cross(PDD a,PDD b) {
	return a.x * b.y - a.y * b.x;
}
double area(PDD a,PDD b,PDD c) {
	return cross(b - a, c - a);
}
double andrew() {
	sort(q, q + n);
	int top = 0;
	for(int i = 0;i < n;i ++ ) {
		while(top >= 2 && area(q[stk[top - 1]], q[stk[top]] , q[i]) <= 0 ){
			if (area(q[stk[top-1]], q[stk[top]], q[i]) < 0 ) 
				used[stk[top -- ]] = false;
			else top --;
		}
		stk[++top] = i;
		used[i] = true;
	}
	used[0] = false;
	for(int i=n-1;i>=0;i--) {
		if(used[i]) continue;
		while(top >= 2 && area(q[stk[top- 1]], q[stk[top]],q[i]) <= 0) 
			top -- ;
		stk[++ top] = i;
	}
	double res = 0;
}

AcWing2935

这个题目看到的第一眼就把我吓到了,这他妈什么狗逼题目,但是看了题解发现,其实这题真的很简单,没有什么特别狗屁的点,完全就是将你搞不了的,转化成你搞得了的东西,然后就能做了,发现一些不变的量和规律就行;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double, double> PDD:
const int N = 40010;
const double pi = acos(-1); 
int n, cnt;
PDD q[N];
int stk[N], top;
bool used[N];
PDD rotate(PDD a,double b) {
	return {a.x*cos(b) + a.y * sin(b), -a.x * sin(b) + a.y * cos(b)};
}
PDD operator - (PDD a, PDD b){
	return {a.x - b.x, a.y - b.y};
}
double cross(PDD a, PDD b) {
	return a.x * b.y - a.y * b.x;
}
double area(PDD a, PDD b, PDD c) {
	return cross(b - a, c - a);
}
double get_dist(PDD a,PDD b) {
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx * dx + dy * dy);
}
double andrew() {
	sort(q , q + cnt);
	for(int i=0;i < cnt;i ++) {
		while(top >= 2 && area(q[stk[top -1]], q[stk[top]], q[i]) <= 0 ) {
			if(area(q[stk[top - 1]], q[stk[top]], q[i]) < 0) 
				used[stk[top --]] = false;
			else top --;
		}
		stk[++top] = i ;
		used[i] = true;
	}
	double res = 0;
	for(int i =2; i <= top; i ++){
		res += get_dist(q[stk[i-1]],q[stk[i]]);
	}
	return res;
}
int main()
{
	scanf("%d",&n);
	double a, b, r;
	scanf("%lf%lf%lf",&a,&b,&r);
	a = a / 2 - r, b = b / 2 - r;
	int dx[] = {1, 1, -1, -1}, dy[] = {1, -1, - 1,1};
	while (n -- ) {
		double x, y, z;
		scanf("%lf%lf%lf",&x,&y,&z);
		for(int i=0;i<4;i++) {
			auto t = rotate({dx[i]*b,dy[i]*a} ,-z);
			q[cnt ++ ] = {x + t.x, y + t.y};
		}
	}
	double res = andrew();
	printf("%.2lf\n",res + 2 *pi * r );
}

AcWing2803

半平面交。就是先排序一下,然后双端队列维护凸包上的边,最后队头更新队尾,队尾更新队头即可;
这个比维护前面的那个凸包稍微要好理解一点;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double ,double > PDD;
const int N = 510;
const double eps = 1e-8;
int cnt;
struct Line {
	PDD st, ed;
}line[N];
int q[N];
int sign(double x) {
	if(fabs(x) < eps) return 0;
	if(x < 0) return -1;
	return 1;
}
int dcmp(double x,double y) {
	if(fabs(x - y) < eps) return 0;
	if(x < y) return -1;
	return 1;
}
double get_angle(const Line& a){
	return atan2(a.ed.y - a.st.y, a.ed.x - a.st.x);
}
bool cmp(const Line& a,const Line &b) {
	double A = get_angle(a), B = get_angle(b);
	if(!dcmp(A, B)) return area(a.st,a.ed,b.ed) < 0;
	return A < B;
}
PDD operator - (PDD a,PDD b) {
	return {a.x - b.x , a.y - b.y};
}
double cross(PDD a, PDD b) {
	return a.x * b.y - a.y * b.x;
}
double area(PDD a,PDD b,PDD C) {
	return cross(b - a, c - a);
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w) {
	auto u = p - q;
	double t = cross(w, u) / cross(v, w);
	return {p.x + v.x * t , p.y + v.y * t};
}
PDD get_line_intersection(Line a,Line b) {
	return get_line_intersection(a.st, a.ed - a.st , b.st, b.ed - b.st);
}
bool on_right(Line &a,Line &b, Line &c) {
	auto o = get_line_intersection(b,c);
	return sign(area(a.st, a.ed ,o)) <= 0;
}
double half_plane_intersection() {
	sort(line,line + cnt ,cmp);
	int hh = 0, tt = -1;
	for(int i=0;i < cnt; i ++) {
		if(i && !dcmp(get_angle(line[i]), get_angle(line[i-1]))) continue;
		while(hh + 1 <= tt && on_right(line[i],line[q[tt-1]], line[q[tt]])) tt --;
		while(hh + 1 <= tt && on_right(line[i],line[q[hh]], line[q[hh+1]])) hh ++;
		q[++tt] = i;
 	}
 	while(hh + 1 <= tt && on_right(line[q[hh]],line[q[tt-1]],line[q[tt]])) tt --;
 	while(hh + 1 <= tt && on_right(line[q[tt]],line[q[hh]],line[q[hh+1]])) hh ++;
 	
 	q[++tt] = q[hh];
 	int k = 0 ;
 	for(int i=hh;i < tt;i ++) 
 		ans[k ++ ] = get_line_intersection(line[q[i]],line[q[i+1]]);
 	double res = 0;
 	for(int i=1;i < k ;i++) 
 		res += area(ans[0], ans[i],ans[i+1]);
 	return res / 2;
}
int main()
{
	int n, m;
	scanf("%d",&n);
	while(n --) {
		scanf("%d",&m);
		for(int i=0;i<m;i++) scanf("%lf%lf",&pg[i].x,&pg[i].y);
		for(int i=0;i<m;i++) 
			line[cnt ++ ] = {pg[i], pg[(i+1) % m]};
	}
	double res = half_plane_intersection();
	printf("%.3lf\n",res);
	return 0;
}

AcWing2957

啊这,我第一眼想到的是,可以用一个堆来维护,每次将可能会产生超越事件的车塞入里面,用链表储存,然后每次的第一名就是答案的集合。
但是放在计算几何里面,不免让人想到图像,不免让人想到图像,然后发现这正是一种半平面交,然后就行了。

AcWing3028

板子题没啥好说的;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double, double> PDD;
const int N = 100010;
const double eps = 1e-12;
const double PI = acos(-1);
int n;
PDD q[N];
struct circle {
	PDD p;
	double r;
};
int sign(double x) {
	if ( fabs(x) < eps ) return 0;
	if (x < 0) return -1;
	return 1;
}
int dcmp(double x,double y) {
	if ( fabs(x - y) < eps ) return 0;
	if ( x < y ) return -1;
	return 1;
}
PDD operator - (PDD a, PDD b) {
	return {a.x - b.x, a.y - b.y};
}
PDD operator + (PDD a,PDD b) {
	return {a.x + b.x, a.y + b.y};
}
PDD operator* (PDD a, double t) {
	return {a.x * t, a.y * t};
}
double operator *(PDD a,PDD b) {
	return a.x * b.y - a.y * b.x;
}
PDD operator / (PDD a,double t) {
	return {a.x / t ,a.y / t};
}
PDD rotate(PDD a,double b) {
	return {a.x * cos(b) + a.y * sin(b), - a.x * sin(b) + a.y * cos(b)};
}
double get_dist(PDD a,PDD b) {
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx * dx + dy * dy);
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w) {
	auto u = p - q;
	double t = w* u / (v *w);
	return p + v * t;
}
pair<PDD,PDD> get_line(PDD a,PDD b) {
	return {(a+b) / 2, rotate(b - a ,PI / 2)};
}
circle get_circle (PDD a,PDD b,PDD c) {
	auto u = get_line(a, b), v = get_line(a,c);
	auto p = get_line_intersection(u.x, u.y, v.x, v.y);
	return {p, get_dist(p, a)};
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i < n;i ++) scanf("%lf%lf",&q[i].x,&q[i].y);
	random_shuffle(q, q+n);
	circle c({q[0], 0});
	for(int i=1;i<n;i++){
		if(dcmp(c.r, get_dist(c.p , q[i])) < 0 ) {
			c = {q[i],0 };
			for(int j = 0; j < i ; j ++ ){ 
				if(dcmp(c.r, get_dist(c.p, q[j])) < 0 ) {
					c = {(q[i] + q[j]) / 2, get_dist(q[i], q[j]) / 2};
					for(int k = 0;k < j; k ++ ) {
						if (dcmp(c.r, get_dist(c.p, q[k])) < 0) 
							c = get_circle(q[i], q[j], q[k]);
					}
				}
			}
		}
	}
	printf("%.10lf\n",c.r);
	printf("%.10lf $.10lf\n" , c.p.x, c.p.y);
	return 0;
}

AcWing2785

映射一下就出来了,真的是妙死我了;
运用各种手段或者说是改造现有模型使两者契合;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double,double> PDD;
const int N = 5010;
const double eps = 1e-12;
const double PI = acos(-1);
int n;
PDD q[N];
struct circle {
	PDD p;
	double r;
};
int sign(double x) {
	if( fabs(x) < eps) return 0;
	if(x < 0) return -1;
	return 1;
}
int dcmp(double x,double y) {
	if(fabs(x - y) < eps) return 0;
	if(x < y) return -1;
	return 1;
}
PDD operator + (PDD a, PDD b) {
	return {a.x + b.x, a.y + b.y};
}
PDD operator - (PDD a, PDD b) {
	return {a.x - b.x, a.y - b.y};
}
PDD operator* (PDD a, double t) {
	return {a.x * t, a.y *t};
}
PDD operator / (PDD a,double t) {
	return {a.x / t, a.y / t};
}
double operator*(PDD a,PDD b) {
	return  a.x * b.y - a.y * b.x;
} 
PDD rotate(PDD a,double b) {
	return {a.x * cos(b) + a.y * sin(b) , - a.x * sin(b) + a.y * cos(b)};
}
double get_dist(PDD a,PDD b) {
	double dx = a.x  -b.x;
	double dy = a.y - b.y;
	return sqrt(dx * dx + dy *dy);
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w){
	auto u = p - q;
	double t = w * u / (v * w);
	return p + v * t;
}
pair<PDD,PDD> get_line(PDD a,PDD b) {
	return {(a + b) / 2, rotate(b- a, PI / 2)};
}
PDD get_circle(PDD a,PDD b,PDD c) {
	auto u = get_line(a, b), v = get_line(a, c);
	auto p = get_line_intersection(u, v);
	return {p ,get_dist(p, a)};
}
int main() 
{
	scanf("%d",&n);
	for(int i = 0; i < n; i ++ )  scanf("%lf%lf",&q[i].x, &q[i].y);
	double a, p;
	for(int i= 0 ;i < n; i ++) {
		q[i] = rotate(q[i], a / 180 * PI);
		q[i].x /= p;
	}	
	random_shuffle(q, q+n);
	circle c({q[0], 0});
	for(int i=1;i< n;i++) 
		if(dcmp(c.r , get_dist(c.p, q[i])) < 0) {
			c = {q[i], 0};
			for(int j = 0; j < i ; j ++) 
				if(dcmp(c.r, get_dist(c.p, q[j])) < 0) {
					c = {(q[i] + q[j]) / 2,get_dist(q[i],q[j]) / 2};
					for(int k =0;k < j; k ++ )
						if(dcmp(c.r, get_dist(c.p, q[k])) < 0)
							c = get_circle(q[i], q[j], q[k]);
				}
		}
	
	printf("%.3lf\n",c.r);
	
	return 0;
}

AcWing3068

扫描线思想,还是非常不错的;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define x first
#define y second;
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 1010;
int n;
PII l[N], r[N];
PII q[N];

LL range_area(int a,int b) {
	int cnt = 0;
	for(int i = 0 ;i < n; i ++ ) 
		if(l[i].x <= a && r[i].x >= b) 
			q[cnt ++ ] = {l[i].y,r[i].y};
	if(!cnt) return 0;
	sort(q, q+cnt);
	LL res = 0;
	int st = q[0].x, ed = q[0].y;
	for(int i=1; i < cnt ;i ++ ) {
		if(q[i].x <= ed) ed = max(ed, q[i].y);
		else {
			res += ed - st;
			st = q[i].x, ed = q[i].y;
		}
	} 
	res += ed - st;
	return res * (b - a);
}
int main()
{
	scanf("%d",&n);
	vector<int> xs;
	for(int i=0;i<n;i++) {
		scanf("%d%d%d%d",&l[i].x, &l[i].y, &r[i].x, &r[i].y);
		xs.push_back(l[i].x), xs.push_back(r[i].x);
	}
	sort(xs.begin(), xs.end());
	LL res = 0;
	for(int i=0;i + 1 < xs.size(); i ++) 
		if( x[i] != x[i+1]) 
			res += range_area(x[i], x[i+1]);
	printf("%lld\n",res);
	return 0;
}

AcWing2801

我盲猜一发直接化成是可以积的梯形。
果然是的;
好题;

#include<iostream>
#include<cstdio>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<double ,double > PDD;
const N = 110;
const double eps = 1e-8, INF = 1e6; 
int n;
PDD tr[N][3];
PDD q[N];
int sign(double x) {
	if(fabs(x) < eps) return 0;
	if(x < 0) return -1;
	return 1;
}
int dcmp(double x,double y) {
	if(fabs(x - y) < eps) return 0;
	if(x < y) return -1;
	return 1;
}
PDD operator + (PDD a,PDD b) {
	return {a.x + b.x, a.y +b.y};
}
PDD operator - (PDD a,PDD b) {
	return {a.x - b.x ,a.y - b.y};
}
PDD operator* (PDD a,double t) {
	return {a.x * t ,a.y * t};
}
double operator * (PDD a,PDD b) {
	return a.x * b.y - a.y * b.x;
}
double operator& (PDD a,PDD b) {
	return a.x * b.x + a.y * b.y;
}
bool on_segment(PDD p,PDD a,PDD b) {
	return sign((p-a) & (p-b)) <= 0;
}
double line_area(double a,int side) {
	int cnt = 0;
	for (int i=0;i < n; i ++) {
		auto t = tr[i];
		if(dcmp(t[0].x , a) > 0 || dcmp(t[2].x,a) < 0) continue;
		if(!dcmp(t[0].x,a ) && !dcmp(t[1].x,a)) {
			if(side) q[cnt ++] = {t[0].y, t[1].y};
		}else if(!dcmp(t[2].x, a) && !dcmp(t[1].x,a)) {
			if(!side) q[cnt ++] = (t[2].y, t[1].y);
		}else {
			double d[3];
			int u = 0;
			for(int j=0;j<3;j++) {
				auto o = get_line_intersection(t[j],t[(j+1) %3] - t[j],{a,-INF},{0,INF*2});
				if(dcmp(o.x, INF)) d[u ++ ] = o.y;
			}
			if(u) {
				sort(d,d+u);
				q[cnt ++ ] = {d[0], d[u-1]};
			}
			
		}
	}
	if(!cnt) return 0;
	for(int i = 0; i < cnt; i ++ ) {
		if(q[i].x > q[i].y) 
			swap(q[i].x, q[i].y);
	}
	sort(q, q+cnt);
	double res = 0 , st = q[0].x, ed = q[0].y;
	for(int i =1;i < cnt; i ++) {
		if(q[i].x <= ed) ed = max(ed, q[i].y); 
		else {
			res += ed- st;
			st = q[i].x, ed =q[i].y;
		}
	}
}
double range_area(double a,double b) {
	return ( line_area(a,1) + line_area(b,0) ) * (b - a) / 2; 
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w) {
	if(!sign(v*w)) return {INF,INF};
	auto u = p - q;
	auto t = w * u / (v * w);
	auto o = p + v * t;
	if(!on_segment(o ,p ,p + v) || !on_segment(o, q, q+w))
		return {INF ,INF};
	return o;
}
int main()
{
	scanf("%d",&n);
	vector<double> xs;
	for(int i=0;i < n;i++) {
		for(int j=0;j<n;j++) {
			scanf("%lf%lf",&tr[i][j].x, &tr[i][j].y);
			xs.push_back(tr[i][j].x);	
		}
		sort(tr[i] ,tr[i] + 3);
	}
	for(int i=0;i<n;i++) 
		for(int j=i+1;j<n;j++)
			for(int x =0;x<3;x ++) 
				for(int y=0;y<3;y++) {
					auto o = get_line_intersection(tr[i][x], tr[i][(x+1) % 3] - tr[i][x],
					tr[j][y],tr[j][(y+1) %3] - tr[j][y]);
					if(dcmp(o.x,INF))
						xs.push_back(o.x);
				}
	sort(xs.begin(),xs.end());
	double res = 0;
	for(int i=0;i + 1 < xs.size(); i ++) 
		if(dcmp(xs[i], xs[i+1]))
			res += range_area(xs[i], xs[i+1]);
	printf("%.2lf\n",res);
	return 0;
}

AcWing3074

自适应辛普森积分;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps = 1e-12;
double f(double x){
	return sin(x) / x;
}
double simpson(double l,double r) {
	auto mid = (l + r) / 2;
	return (r-l) * (f(l) + 4 * f(mid) + f(r)) / 6;
}
double asr(double l,double r,double s) {
	auto mid = (l + r) / 2;
	auto left = simpson(l ,mid), right = simpson(mid, r);
	if(fabs(left + right - s) < eps) return left + right;
	return asr(l,mid,left) + asr(mid,r,right);
}
int main() 
{
	double l ,r;
	scanf("%lf%lf",&l,&r);
	printf("%lf\n",asr(l,r,simpson(l,r)));
	return 0;
}

AcWing3069

自适应辛普森积分在求解积分时候的作用。
图中要求的是面积 —>函数---->积分---->辛普森积分,就是这么一个思路;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps = 1e-12;
double f(double x){
	return sin(x) / x;
}
double simpson(double l,double r) {
	auto mid = (l + r) / 2;
	return (r-l) * (f(l) + 4 * f(mid) + f(r)) / 6;
}
double asr(double l,double r,double s) {
	auto mid = (l + r) / 2;
	auto left = simpson(l ,mid), right = simpson(mid, r);
	if(fabs(left + right - s) < eps) return left + right;
	return asr(l,mid,left) + asr(mid,r,right);
}
int main() 
{
	double l ,r;
	scanf("%lf%lf",&l,&r);
	printf("%lf\n",asr(l,r,simpson(l,r)));
	return 0;
}

AcWing2938

旋转卡壳;
以下是CE代码;

#include<iostream>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N = 50010;
int n;
PII q[N];
int stk[N], top;
bool used[N];
int operator *(PDD a,PDD b) {
	return a.x * b.y - a.y * b.x;
}
int area(PDD a,PDD b,PDD c){
	return (b - a) * (c - a);
}
int get_dist(PDD a,PDD b) {
	int dx = a.x - b.x;
	int dy = a.y - b.y;
	return dx*dx + dy * dy;
}
void get_convex()
{
	sort(q, q+n);
	for(int i=0;i<n;i++) {
		while(top >= 2 && area(q[stk[top - 2]], q[stk[top - 1]], q[i]) <= 0)
			used[stk[ -- top]] = false;
		stk[top ++ ] = i;
		used[i] = true;
	}
	used[0] = false;
	for(int i=n-1;i >= 0 ;i -- ) {
		if (used[i]) continue;
		while (top >= 2 && area(q[stk[top - 2]], q[stk[top - 1]], q[i]) <= 0)
			top -- ;
		stk[top ++ ] = i;
	}
	top -- ;
}
int rotating_calipers() {
	int res = 0;
	for(int i=0 , j = 2;i < top;i ++ ) {
		auto d = q[stk[i]], e = q[stk[i + 1]];
		while(area(d,e,q[stk[j]]) < area(d,e,q[stk[j+1]])) j = (j + 1) % top;
		res = max(res,max(get_dist(d, q[stk[j]]),get_dist(e,q[stk[j]])));	
	}
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i ++) scanf("%d%d",&q[i].x,&q[i].y);
	get_convex();
	printf("%d\n",rotating_calipers());
}

AcWing2142

居然给我想出来了,那是不是说明有点水;
直接旋转卡壳,然后每次都去求一下两条平行线的间距,然后再求一下长就行了好像是的,不过细节要丰富一点;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double ,double> PDD;
const int N = 50010;
const double eps = 1e-12, INF = 1e20;
const double PI = acos(-1);
int n;
PDD q[N];

int sign(double x) {
	if(fabs(x) < eps) return 0;
	if(x < 0 ) return -1;
	return 1;
}
int dcmp(double x,double y) {
	if(fabs(x -y) < eps) return 0;
	if(x < y) return -1;
	return 1;
}
PDD operator + (PDD a,PDD b) {
	return {a.x + b.x, a.y + b.y};
}
PDD	operator - (PDD a,PDD b) {
	return {a.x - b.x, a.y - b.y};
}
PDD operator* (PDD a,double t) {
	return {a.x * t	,a.y * t};
}
PDD operator/ (PDD a, double t) {
	return {a.x / t, a.y / t};
}
double operator*(PDD a,PDD b) {
	return a.x * b.x  + a.y * b.y;
} 
double operator& (PDD a, PDD b) {
	return a.x * b.x + a.y * b.y;
}
double area(PDD a,PDD b,PDD c ) {
	return (b - a) * (c - a);
}
double get_len(PDD a) {
	return sqrt(a & a);
}
double project(PDD a,PDD b,PDD c) {
	return ((b - a) & (c - a)) / get_len(b -a);
}
void get_convex() {
	sort(q, q+n);
	for(int i=0;i < n; i++ ){
		while(top >= 2 && dcmp(area(q[stk[top - 2]], q[stk[top - 1]], q[i])) <= 0) 
			used[stk[ -- top ]] = false;
		stk[top ++ ] = i;
		used[i] = true;
	}
	used[0] = false;
	for(int i=n-1;i >= 0 ; i -- ) {
		if(used[i]) continue;
		while (top >= 2 && dcmp(area(q[stk[top - 2]], q[stk[top - 1]], q[i])) <= 0) 
			top -- ;
		stk[top ++] = i;
	}
	top -- ;
}
PDD norm(PDD a) {
	return a / get_len(a);
}
PDD rotate(PDD a, double b) {
	return {a.x * cos(b) + a.y * sin(b) , - a.x * sin(b) + a.y * cos(b)};
}
void rotating_calipers() {
	for(int i = 0, a = 2, b = 2, c = 2; i < top; i++ ){
		auto d = q[stk[i]], e = q[stk[i + 1]];
		while(dcmp(area(d, e, q[stk[a]]) , area(d, e, q[stk[a+1]])) <= 0) a = (a + 1) % top;
		while(dcmp(project(d, e, q[stk[b]]), project(d, e, q[stk[b+ 1]])) < 0) b = (b + 1) % top;
		if(!i) c = a;
		while(dcmp(project(d, e, q[stk[c]]), project(d, e, q[stk[c + 1]])) > 0) c = (c + 1) % top;
		auto x = q[stk[a]], y = q[stk[b]], z = q[stk[c]];
		auto h = area(d, e, x) / get_len(e - d);
		auto w = ((y - z) & ( e - d)) / get_len(e - d);
		if(h * w < min_area) {
			min_area = h * w;
			ans[0] = 
		} 
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i < n;i ++ )	scanf("%lf%lf",&q[i].x,&q[i].y);
	get_convex();
	rotating_calipers();
	int k = 0;
	for(int i = 1;i < 4;i ++) 
		if(dcmp(ans[i].y, ans[k].y) < 0 || !dcmp(ans[i].y, ans[k].y) && dcmp(ans[i].x,ans[k].x))
			k = i;
	printf("%.5lf\n",min_area);
	for(int i=0;i <4;i ++, k ++) {
		auto x = ans[k % 4].x, y = ans[k % 4].y;
		if(!sign(x)) x = 0.0;
		if(!sign(y)) y = 0.0;
		printf("%.5lf %.5lf\n",x,y);
	}	
	
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值