CF几何刷题记录
不开坑就没有方向……
(虽然开了坑也会跑路……)
UPD:退役了退役了,跑路跑路
想把CF里过题人数100+的几何题都做一做……
那就先从1000-往下做吧
模板:
#include<bits/stdc++.h>
#define ERROR 1e99
#define N 100005
using namespace std;
typedef double data;
typedef long long LL;
const data PI=acos(-1.);
const data EPS=1E-10;
int Zero(data O){return fabs(O)<=EPS;}
void Read(data &now){double IO;scanf("%lf",&IO);now=IO;}
struct Point{
data x,y;
void read(){Read(x);Read(y);}
void Print(){printf("%lf %lf\n",(double)x,(double)y);}
void init(data _x,data _y){x=_x;y=_y;}
data operator * (const Point &b)const{
return x*b.y-y*b.x;
}
Point operator * (const data &b)const{
return (Point){x*b,y*b};
}
data operator ^ (const Point &b)const{
return x*b.x+y*b.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};
}
int operator < (const Point &b)const{
return x<b.x||x==b.x&&y<b.y;
}
int operator == (const Point &b)const{
return Zero(x-b.x)&&Zero(y-b.y);
}
void Rotate(data alpha){
data _x=x*cos(alpha)-y*sin(alpha);
data _y=y*cos(alpha)+x*sin(alpha);
x=_x;y=_y;
}
}ERRORPOINT=(Point){ERROR,ERROR};
struct Line{
Point p,q;
};
struct Circle{
Point O;data r;
void read(){O.read();Read(r);}
};
data sqr(data x){return x*x;}
data dist(Point A,Point B){return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));}
int Intersection(Line A,Line B,Point &ret){
ret=A.p;
data u=(A.p-B.p)*(B.p-B.q);
data v=(A.p.x-A.q.x)*(B.p.y-B.q.y)-(A.p.y-A.q.y)*(B.p.x-B.q.x);
if (Zero(v)) return 0;u/=v;
ret.x+=(A.q.x-A.p.x)*u;
ret.y+=(A.q.y-A.p.y)*u;
return 1;
}
int Intersection(Circle C,Line L,Point &A,Point &B){
Point p=C.O;p.x+=L.p.y-L.q.y;p.y+=L.q.x-L.p.x;
Intersection((Line){p,C.O},L,p);
data tmp=sqr(C.r)-sqr(p.x-C.O.x)-sqr(p.y-C.O.y);
if (tmp<-EPS) return 0;
data t=sqrt(max(tmp,0.))/dist(L.p,L.q);
A=p+(L.q-L.p)*t;B=p-(L.q-L.p)*t;
return 1;
}
int Intersection(Circle C1,Circle C2,Point &A, Point &B) {
double d = dist(C1.O,C2.O);
if (d<fabs(C1.r-C2.r)-EPS || d>fabs(C1.r+C2.r)+EPS) return 0;
double cosa = (sqr(C1.r) + sqr(d) - sqr(C2.r)) / (2 * C1.r * d);
double sina = sqrt(max(0., 1. - sqr(cosa)));
A = B = C1.O;
A.x += C1.r / d * ((C2.O.x - C1.O.x) * cosa + (C2.O.y - C1.O.y) * -sina);
A.y += C1.r / d * ((C2.O.x - C1.O.x) * sina + (C2.O.y - C1.O.y) * cosa);
B.x += C1.r / d * ((C2.O.x - C1.O.x) * cosa + (C2.O.y - C1.O.y) * sina);
B.y += C1.r / d * ((C2.O.x - C1.O.x) *-sina + (C2.O.y - C1.O.y) * cosa);
return 1;
}
//此处默认data是int
int Polar_cmp(const Point &A,const Point &B){
if (A.y==0&&B.y==0) return A.x>0&&B.x<0;
if (A.y==0) return A.x>0||B.y<0;
if (B.y==0) return B.x<0&&A.y>0;
if ((A.y>0)^(B.y>0)) return A.y>0;
return (A*B)>0;
}
vector<Point>Make_Convex(vector<Point>now){
sort(now.begin(),now.end());
if (now.size()<3) return now;
for (int i=0;i<now.size();i++)
now[i].Print();
puts("");
vector<Point>up;up.clear();
up.push_back(now[0]);
for (int i=1;i<now.size();i++){
for (;up.size()>1&&(up[up.size()-2]-up.back())*(now[i]-up.back())<=0;up.pop_back());
up.push_back(now[i]);
}
vector<Point>down;down.clear();
down.push_back(now.back());
for (int i=now.size()-1;i>=0;i--){
for (;down.size()>1&&(down[down.size()-2]-down.back())*(now[i]-down.back())<=0;down.pop_back());
down.push_back(now[i]);
}
vector<Point>ret;ret.clear();
for (int i=down.size()-1;i>0;i--)
ret.push_back(down[i]);
for (int i=up.size()-1;i>0;i--)
ret.push_back(up[i]);
return ret;
}
//以下是最小圆覆盖
//两个点
Circle make_2(Point p,Point q){
Circle ret;
ret.O.x=(p.x+q.x)/2.0;
ret.O.y=(p.y+q.y)/2.0;
ret.r=dist(ret.O,p);
return ret;
}
//返回三角形的外心
Circle make_3(const Point &a,const Point &b,const Point &c) {
data a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
data a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
data d=a1*b2-a2*b1;
Circle ret;
ret.O.x=a.x+(c1*b2-c2*b1)/d;
ret.O.y=a.y+(a1*c2-a2*c1)/d;
ret.r=dist(ret.O,a);
return ret;
}
bool IN(Circle now,Point a){
return (dist(now.O,a)<=now.r+EPS);
}
Circle Smallest_Circle(Point *a,int n){
if (n==1) return (Circle){a[1],0};
srand(2333);
random_shuffle(a+1,a+n+1);
Circle cur=make_2(a[1],a[2]);
for (int i=2;i<=n;i++)
if (!IN(cur,a[i])){
cur=make_2(a[1],a[i]);
for (int j=1;j<i;j++)
if (!IN(cur,a[j])){
cur=make_2(a[i],a[j]);
for (int k=1;k<j;k++)
if (!IN(cur,a[k]))
cur=make_3(a[i],a[j],a[k]);
}
}
return cur;
}
int main(){
}
【280A】
有一个水平放置的长方形。求它绕中心旋转\(\alpha\)度后的图形与原图形的面积交。
直接分类讨论感觉很烦烦。本来想趁机搞一个HPI的板子,可是放弃了……
后来用了一个比较巧妙的做法。把两个矩形的边框拿来交一交,只有都在两条直线内的交点才会是最终答案凸包上的点。然后随便做个凸包就好了。
【793C】
有一坨给定初始坐标和方向向量的点,还有一个固定的矩形。问一个最早的时刻,使得所有点都严格在矩形内部。
因为是矩形,把直线画出来后,最多只会与矩形交两个unique的点(注意到<2必然是无解)。然后算一算对应时间求个交即可。
此题最大的坑点是“严格”。最后的合法区间如果只有一个值,那么其实是不合法的;这个eps就很耐人寻味。还要注意那些和边框重合的运动路线也是不合法的。
【166B】
分析一下题目性质,转化为问一个点是否严格在一个凸包内部。
分别维护出上凸壳和下凸壳,每次二分后用叉积判断即可。
【600D】
求两个圆面积交……看起来很old,确是一道特别帅气的题……
其实就是求两个弓形的面积。我本来是直接求交点,用叉积计算三角形面积,然后余弦定理算一下扇形的角度。
但是数据范围太大了,求交的精度会爆炸……
其实知道了半径和距离后,换个三角形研究,即可算出扇形的角度……然后根据角度再计算三角形。
仔细想想,这也能开发出求交的另一个方法。虽然有三角函数,但感觉精度靠谱很多。
【2C】
对于其中两个圆,可以发现符合要求的点在一条直线或者一个圆上。
那么最终答案就在圆/直线与圆/直线的交点上。检验板子的好题……