区域赛临近,和队友疯狂VP,但是补题补少了,感觉其实有点在找心理安慰的感觉,先简单地整理一下最近做过的计算几何题
ICPC2018青岛站H题(废题,0AC)
这道题的思路其实简单,但是需要大码力,涉及到的细节可以学一学
题目大意:
给定一个三角形障碍物的三个点的坐标,一条线段的起终点坐标表示镜子,以及 A , B A,B A,B点的坐标
现在有 m m m个石头放在 A A A点,要将它们全部运到 B B B点
并且满足如下规则:
1、每次只能带一个石头
2、所有石头只能在B点放下
3、在所有时刻路径上的每个点必须能够看到所有石头
4、障碍物和镜子都不能穿过,镜子只有一面反光
求走过的最短距离
ICPC2020南京站I题(可补)
题目大意:
一个质点从 ( 0 , − i n f ) (0,-inf) (0,−inf)处向上, y y y轴方向的速度给定且恒定,一直运动到 x x x轴上,要求在 x x x坐标的绝对值小于给定的 m m m的情况下,不撞到任何障碍物(障碍物为线段), x x x轴方向速度可以瞬间改变,求满足条件下 x x x轴方向速度的最小限制
ICPC2020沈阳站A题(已AC)
题目大意:
给定一个矩形,和包含在其中的两条线段,求矩形中满足到两条线段距离相等的点的集合组成的面积
思路:
考虑到某条线段距离为d的点的集合,围成的图形应该是形状上为两条线段加上两个半圆的图形。
那么随着d的增大,如果贡献的面积不为0,这两条线段的距离为d的图形的交集应该是一段连续的点。
所以就分为了几种情况
1、两条线段完全重叠,这种情况就是矩形的面积
2、两条线段有一个端点重合且共线
3、两条线段有一个端点重合且不共线
4、两条线段有一部分重叠
根据对应的情况列出对应的直线计算半平面交即可
需要注意的点:
1.eps最好跟题目的相对误差一致,稍微开小一点都可能导致WA
2.极角排序的时候也要考虑eps,不然垂直于x轴的可能会发生错误
3.printf用%Lf输出0会RE
代码如下
#include<bits/stdc++.h>
#define double long double
using namespace std;
//eps开1e-9就能过,开其他的很玄学过不了
const double eps=1e-9;
const double pi=acos(-1.0);
//点和向量类
template<class T>
struct Point{
typedef Point P;
T x,y;
explicit Point(T x=0,T y=0) :x(x),y(y) {}
bool operator <(P p) const {return tie(x,y)<tie(p.x,p.y);}
bool operator ==(P p) const {return abs(x-p.x)<eps&&abs(y-p.y)<eps;}
P operator +(P p) const {return P(x+p.x,y+p.y);}
P operator -(P p) const {return P(x-p.x,y-p.y);}
P operator *(T d) const {return P(x*d,y*d);}
P operator /(T d) const {return P(x/d,y/d);}
T dot(P p) const{return x*p.x+y*p.y;}
T cross(P p) const{return x*p.y-y*p.x;}
T cross(P a,P b) const{return (a-*this).cross(b-*this);}
T dist2() const{return x*x+y*y;}
double dist() const{return sqrt((double)dist2());}
double angle() const{return atan2(y,x);}
P unit() const{return *this/dist();}
P perp() const{return P(-y,x);}
P normal() const{return perp().unit();}
P rotate(double a) const{
return P(x*cosl(a)-y*sinl(a),x*sinl(a)+y*cosl(a));
}
};
template<class P>
int lineIntersection(const P &s1,const P& e1,const P& s2,const P& e2,P &r){
if(abs((e1-s1).cross(e2-s2))>eps){
r=s2-(e2-s2)*(e1-s1).cross(s2-s1)/(e1-s1).cross(e2-s2);
return 1;
}
else
return -(abs((e1-s1).cross(s2-s1))<eps||s2==e2);
}
//直线,半平面类
typedef Point<double> P;
struct Line{
P P1,P2;
explicit Line(P a=P(),P b=P()) :P1(a),P2(b) {};
P intpo(Line y){
P r;
assert(lineIntersection(P1,P2,y.P1,y.P2,r)==1);
return r;
}
P dir(){
return P2-P1;
}
bool contains(P x){
return (P2-P1).cross(x-P1)<eps;
}
bool out(P x){
return !contains(x);
}
};
//极角排序
template<class T>
bool mycmp(Point<T> a,Point<T> b){
if(a.x*b.x<0&&abs(a.x)>eps&&abs(b.x)>eps) return a.x<0;
if(abs(a.x)<eps) {
if(abs(b.x)<eps) return a.y>0&&b.y<0;
if(b.x<0) return a.y>0;
if(b.x>0) return true;
}
if(abs(b.x)<eps){
if(a.x<0) return b.y<0;
if(a.x>0) return false;
}
return a.cross(b)>0;
}
bool cmp(Line a,Line b)
{
return mycmp(a.dir(),b.dir());
}
//半平面交求面积,其中vector<P> s表示半平面交后得到的凸包
double Intersection_Area(vector<Line> b){
sort(b.begin(),b.end(),cmp);
int n=b.size();
int q=1,h=0,i;
vector<Line> c(b.size()+10);
for(i=0;i<n;i++) {
while(q<h&&b[i].out(c[h].intpo(c[h-1]))) h--;
while(q<h&&b[i].out(c[q].intpo(c[q+1]))) q++;
c[++h]=b[i];
if(q<h&&abs(c[h].dir().cross(c[h-1].dir()))<eps) {
h--;
if(b[i].out(c[h].P1)) c[h]=b[i];
}
}
while(q<h-1&&c[q].out(c[h].intpo(c[h-1]))) h--;
while(q<h-1&&c[h].out(c[q].intpo(c[q+1]))) q++;
if(h-q<=1) return 0;
c[h+1]=c[q];
vector<P> s;
for(i=q;i<=h;i++) s.push_back(c[i].intpo(c[i+1]));
s.push_back(s[0]);
double ans=0;
for(i=0;i<(int) s.size()-1;i++) ans+=s[i].cross(s[i+1]);
return ans/2;
}
template<class P>
int segmentIntersection(const P &s1,const P&e1,const P &s2,const P &e2,P &r1,P &r2){
if(e1==s1){
if(e2==s2){
if(e1==e2) {r1=e1;return 1;}
else return 0;
} else return segmentIntersection(s2,e2,s1,e1,r1,r2);
}
P v1=e1-s1,v2=e2-s2,d=s2-s1;
auto a=v1.cross(v2),a1=v1.cross(d),a2=v2.cross(d);
if(abs(a)<eps){
auto b1=s1.dot(v1),c1=e1.dot(v1),
b2=s2.dot(v1),c2=e2.dot(v1);
if(a1||a2||max(b1,min(b2,c2))>min(c1,max(b2,c2)))
return 0;
r1=min(b2,c2)<b1?s1:(b2<c2?s2:e2);
r2=max(b2,c2)>c1?e1:(b2>c2?s2:e2);
return 2-(r1==r2);
}
if(a<0) {a=-a;a1=-a1;a2=-a2;}
if(0<a1||a<-a1||0<a2||a<-a2)
return 0;
r1=s1-v1*a2/a;
return 1;
}
template<class P>
bool onSegment(const P& s,const P& e,const P& p){
P ds=p-s,de=p-e;
return abs(ds.cross(de))<eps&&ds.dot(de)<=0;
}
Point<double> ul,ur,dl,dr,s1,e1,s2,e2;
typedef Point<double> P;
vector<Line> Area;
double calang(P o,P s1,P s2)
{
P s=o+(s1-o).rotate(pi/2);
if(((Line){s,o}).out(s1)) Area.push_back((Line){s,o});
else Area.push_back((Line){o,s});
s=o+(s2-o).rotate(pi/2);
if(((Line){s,o}).out(s2)) Area.push_back((Line){s,o});
else Area.push_back((Line){o,s});
return Intersection_Area(Area);
}
void work()
{
double ans;
Area.clear();
scanf("%Lf%Lf%Lf%Lf",&dl.x,&dl.y,&ur.x,&ur.y);
scanf("%Lf%Lf%Lf%Lf",&s1.x,&s1.y,&e1.x,&e1.y);
scanf("%Lf%Lf%Lf%Lf",&s2.x,&s2.y,&e2.x,&e2.y);
s1=s1-dl;e1=e1-dl;s2=s2-dl;e2=e2-dl;
ur=ur-dl;dl=(P){0,0};
dr=(P){ur.x,dl.y},ul=(P){dl.x,ur.y};
Area.push_back((Line){ul,ur});Area.push_back((Line){ur,dr});
Area.push_back((Line){dr,dl});Area.push_back((Line){dl,ul});
if(e1<s1) swap(s1,e1);
if(e2<s2) swap(s2,e2);
if((s1-e1).dist2()>(s2-e2).dist2()) swap(s1,s2),swap(e1,e2);
if(s1==s2&&e1==e2) {printf("%.10Lf\n",(ur.y-dl.y)*(ur.x-dl.x));return;}
Point<double> r1,r2;
if(segmentIntersection(s1,e1,s2,e2,r1,r2)==0) {printf("0\n");return;}
if(segmentIntersection(s1,e1,s2,e2,r1,r2)==1)
{
if(s1==s2) ans=calang(s1,e1,e2);
else if(s1==e2) ans=calang(s1,s2,e1);
else if(e1==s2) ans=calang(s2,e2,s1);
else if(e1==e2) ans=calang(e1,s1,s2);
else ans=0;
printf("%.10Lf\n",ans);return;
}
if(segmentIntersection(s1,e1,s2,e2,r1,r2)==2)
{
if(r2<r1) swap(r1,r2);
if(r1==s1&&r2==e1)
{
if(s1==s2)
{
P s=e1+(s1-e1).rotate(pi/2);
if((Line){r2,s}.contains(r1)) Area.push_back((Line){r2,s});
else Area.push_back((Line){s,r2});
printf("%.10Lf\n",Intersection_Area(Area));return;
}
if(e1==e2)
{
P s=r1+(e1-r1).rotate(pi/2);
if((Line){r1,s}.contains(r2)) Area.push_back((Line){r1,s});
else Area.push_back((Line){s,r1});
printf("%.10Lf\n",Intersection_Area(Area));return;
}
}
P p1=r1+(r2-r1).rotate(pi/2),p2=r2+(r1-r2).rotate(pi/2);
if((Line){p1,r1}.contains(r2)) Area.push_back((Line){p1,r1});
else Area.push_back((Line){r1,p1});
if((Line){p2,r2}.contains(r1)) Area.push_back((Line){p2,r2});
else Area.push_back((Line){r2,p2});
printf("%.10Lf\n",Intersection_Area(Area));return;
}
}
signed main()
{
//cin.tie(0);
//cin.sync_with_stdio(0);
int t=1;
scanf("%d",&t);
while(t--)
work();
return 0;
}
ICPC2020昆明站I题(已AC)
题目大意:
火车沿线段从 s s s向 t t t运动,在轨道的一侧有 n n n个风车,当火车到达某一点的时候,风车的相对方向会发生改变。例如: a a a原本在 b b b的左边,火车运动到某一点时,从火车的视角, a a a出现在了 b b b的右边。 m m m次询问,每次询问输出一个坐标,恰好在此坐标对于 h i h_i hi风车而言,恰好发生了 k i k_i ki次方向改变
n < = 1000 n<=1000 n<=1000
思路:
直接 O ( n 2 ) O(n^2) O(n2)计算出两两风车连线与线段 S T ST ST的交点,然后按照到 s s s的距离排序即可
需要注意的点:
1.依然是eps,跟相对误差一直或者少一个数量级,不要太小也不要太大
代码如下
#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int maxn=1005;
const double eps=1e-6;//一生之敌
template<class T>
struct Point{
typedef Point P;
T x,y;
explicit Point(T x=0,T y=0) :x(x),y(y) {}
bool operator <(P p) const {return tie(x,y)<tie(p.x,p.y);}
bool operator ==(P p) const {return abs(x-p.x)<eps&&abs(y-p.y)<eps;}
P operator+(P p) const {return P(x+p.x,y+p.y);}
P operator-(P p) const {return P(x-p.x,y-p.y);}
P operator*(T d) const {return P(x*d,y*d);}
P operator/(T d) const {return P(x/d,y/d);}
T dot(P p) const {return x*p.x+y*p.y;}
T cross(P p) const {return x*p.y-y*p.x;}
T cross(P a,P b) const {return (a-*this).cross(b-*this);}
T dist2() const {return x*x+y*y;}
double dist() const {return sqrt((double)dist2());}
double angle() const {return atan2(y,x);}
P unit() const {return *this/dist();}
P perp() const {return P(-y,x);}
P normal() const {return perp().unit();}
P rotate(double a) const{
return P(x*cos(a)-y*sin(a),x*sin(a)+y*cos(a));}
};
template<class P>
int lineIntersection( const P& s1,const P& e1,const P& s2,const P& e2,P& r)
{
if((e1-s1).cross(e2-s2)){
r=s2-(e2-s2)*(e1-s1).cross(s2-s1)/(e1-s1).cross(e2-s2);
return 1;
}
else return -(abs((e1-s1).cross(s2-s1)<eps)||s2==e2);
}
template<class P>
bool onSegment(const P& s,const P& e,const P& p) {
P ds=p-s,de=p-e;
return abs(ds.cross(de))<eps&&ds.dot(de)<=0;
}
vector<Point<double> > vec[maxn];
Point<double> nod[maxn],S,T;
bool cmp(Point<double> a,Point<double> b)
{
return (a-S).dist2()<(b-S).dist2();
}
int n,m;
void work()
{
cin>>n>>m;
cin>>S.x>>S.y>>T.x>>T.y;
for(int i=1;i<=n;i++)
{
double x,y;
cin>>x>>y;
nod[i]=(Point<double>){x,y};
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) continue;
Point<double> res;
int flag=lineIntersection(nod[i],nod[j],S,T,res);
if(flag==1)
{
if(onSegment(S,T,res))
{
vec[i].push_back(res);
}
}
}
for(int i=1;i<=n;i++)
sort(vec[i].begin(),vec[i].end(),cmp);
while(m--)
{
int h,k;
cin>>h>>k;
if(vec[h].size()<k) printf("-1\n");
else
{
printf("%.12Lf %.12Lf\n",vec[h][k-1].x,vec[h][k-1].y);
}
}
}
signed main()
{
//cin.tie(0);
//cin.sync_with_stdio(0);
int t=1;
//cin>>t;
while(t--)
work();
return 0;
}
ICPC2021澳门站C题(已AC)
题目大意:
给定 n n n个点坐标,任意两两之间有连线(障碍),问最少删掉多少个点,使得能从原点移动到无穷远处
思路:
原点应该是删除后点的集合中凸包上的点,我们只需要保证过原点的某条直线将剩下的所有点全都分在同一面即可
因此我们直接将n个点按极角排序,然后双指针求最少的点的数量即可
需要注意的点:
1.计算几何能使用int尽量不用double
2.int下的极角排序用叉积判定即可(事实上我用的MIT的板子也是这样做的,但当时觉得这题过于简单直接自己手写WA了好多发)
代码如下
#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#define int long long
using namespace std;
const int maxn=2e6+20;
struct Point
{
int x,y;
}p[maxn];
int n;
Point o;
inline int cross(Point a,Point b,Point c)
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
bool cmp(Point a,Point b)
{
if(a.y*b.y<0)return a.y>b.y;
if(b.x*a.y==a.x*b.y)return a.x<b.x;
return b.x*a.y<a.x*b.y;
}
void work()
{
cin>>n;
o=(Point){0,0};
for(int i=1;i<=n;i++)
cin>>p[i].x>>p[i].y;
if(n==1||n==2){
cout<<"0\n";return;
}
sort(p+1,p+n+1,cmp);
for(int i=n+1;i<=n*2;++i)
p[i]=p[i-n];
int ans=0;
for(int i=1,j=1;i<=n;)
{
while(j+1-i<n&&j+1<=n*2&&cross(p[i],p[j+1],o)>=0) j++;
ans=max(ans,j-i+1);
i++;
if(j<i)j=i;
}
cout<<n-ans<<"\n";
for(int i=1;i<=n;i++)
p[i].x=0,p[i].y=0;
}
signed main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t;
cin>>t;
while (t--)
work();
return 0;
}
CCPC2021桂林网络赛F题(可补)
题目大意:
给定一个大凸包和一个小凸包,大凸包严格包围小凸包
在大凸包的边上随机放一盏灯,求小凸包被照到的期望长度
思路:
回忆了一下,我的思路过程是这样的
1.小凸包被照到的范围一定是在某两个顶点中间
2.对于某个顶点能被照到的区域取决于它所在边的直线
于是就想到了将小凸包的所有线段外延,求与大凸包的交点,这些交点分割的区域对应了不同的照耀范围,累加一下即可
ICPC2021昆明站L题(可补)
题目大意:
在一个无限的二维平面上有 n n n颗星星,每颗星星都发出相同的不相交的光区域(所有星星都向相同的方向集合发出光线)。光的每个区域都以角度的形式给出。对于每一颗星星,求它被其他星星照亮的次数。星星不会遮挡光线。
如图:
思路:
对于每一个连续的角度区域,我们将它们转到以 x x x轴为起始边
设终边的直线与 y y y轴的截距为 d d d
点 a a a被点 b b b照到实际上就是
a x < b x a_x<b_x ax<bx且 d x > d y d_x>d_y dx>dy(锐角)
a x < b x a_x<b_x ax<bx且 d x < d y d_x<d_y dx<dy(钝角)
转化为这样的话,做二维偏序即可
需要注意的点
1.VP时我不会二维偏序,把计算几何部分做完了扔给队友,结果没有跟他说清楚这东西是实数导致GG
ICPC2022杭州站J题
题目大意:
给定 W W W,从 y y y轴上的某个点向 x = W x=W x=W依次画 n n n条线段,如果画画的过程中碰到了之前的画就直接停止画这条线段
一些线段一画完它就把它擦掉,询问对于每一次画画,会在哪个点终止
ICPC2022南京站M题(已AC)
题目大意:
给定一个简单多边形,将它装满水,水往低处流,问至少需要安装多少个出水阀门能将水全部排干
思路:
我们逆时针遍历这个多边形,容易发现只有向量从左到右,且上一个向量向下后一个向量向上时需要安装出水阀门
需要注意的点:
1.遇到平行于 x x x轴的向量continue
2.题目不保证邻边不平行,需要做处理
3.犯了老毛病,没理清思路,没证明好是正确的就盲目上机去写,发现从左到右,且上一个向量向下后一个向量向上时才是正确的
代码如下
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
struct Point
{
int x,y;
Point operator-(Point p) const{return (Point){x-p.x,y-p.y};}
Point operator-() const{return (Point){-x,-y};}
int dot(Point p) const{return x*p.x+y*p.y;}
int cross(Point p) const{return x*p.y-y*p.x;}
}a[maxn];
int n;
bool sflag,lesflag;
bool angcmp(Point e1,Point e2)
{
if(e1.y<0||e2.y<0) return false;
if(e1.x==0&&e2.x==0)
return e1.dot(e2)>0;
if(e1.cross(e2)<0) return true;
else return false;
}
void work()
{
int i=2,ans=0;
while(!((a[i]-a[i-1]).y)) i++;
//从不平行于x轴的边开始
for(;i<=n+1;i++)
{
Point e1=a[i]-a[i-1],e2;
e2=a[i+1]-a[i];
sflag=false;lesflag=false;
while(!(e2.y)&&i<=2*n)
{
i++;sflag=true;
e2=a[i+1]-a[i];
if(a[i].x<a[i-1].x) lesflag=true;
}
//while循环找到下一个不平行于x轴的边
if(sflag)
{
if(!lesflag&&(-(e1.y)>0&&e2.y>0)) ans++;
continue;
}
if(angcmp(-e1,e2)) ans++;
}
cout<<ans<<'\n';
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].x>>a[i].y;
for(int i=n+1;i<=2*n;i++)
a[i]=a[i-n];
work();
return 0;
}
2023香港站G题(可补)
题目大意:
给定一个大扇形的半径 l 1 l_1 l1和角度 α \alpha α,一个小扇形的半径 l 2 l_2 l2和角度 β \beta β
小扇形的圆心是大扇形的弧上的任意一点,求它们覆盖的面积
注意其中 1 < = l 2 < = l 1 < = 1 e 9 1<=l_2<=l_1<=1e9 1<=l2<=l1<=1e9 , α \alpha α为锐角, β \beta β小于 π \pi π
思路:
梦回高中物理磁场题,什么旋转圆和旋转扇形
先把题目读明白,
l
2
<
=
l
1
l_2<=l_1
l2<=l1,然后根据角度直接分类讨论计算即可