「专题总结」半平面交(6/8completed)

还是题都没做完就来写一部分题解了,因为最近很忙(其实是效率太低),所以可能一时半会回不来这个专题。

为了防止什么都没剩下忘干净,于是乎瞎写个题解记录一下。。。

 

还是一如既往的不擅长几何。

刚开始我看到半平面交我还以为是用来解决三维计算几何问题的,吓一跳。

半平面不用多说,就是一条直线把一个二维平面分成两半,其中的一半。

半平面交其实很好理解,就是同一个二维平面中若干个半平面相交的部分。

维护的方法其实挺草率的,和凸包有不少互通的地方,只不过因为封闭所以是双端队列。

凸包偏向于点,半平面交偏向于线。

通常的方法是维护一大堆向量,默认向量的左侧是需要的半平面。

向量的加入顺序通常还是极角,用库函数中的atan2计算。(不要每次都现场算,很慢)

只要队首或队尾的连续两个元素的交点在当前要加入直线的右侧,就把它弹掉就可以。

又因为它是封闭的,所以在全部元素加入完成后你还要认为你把队首尾相连了,即用队尾弹队首,队首弹队尾。

原理不明,实践证明,要先弹队尾后弹队首,否则会出锅。

半平面交从含义上理解是维护了一个凸多边形。但是其实半平面交也可以是无穷平面或其它图形。

处理无穷平面,我们通常在最外侧手动加一个坐标为inf的框(4条向量),这样它最后一定不是无穷平面。

对于最后剩下的依旧不是凸多边形的话,那么队列里的元素一定不超过2个(不封闭,弹队列还弹不掉)

这时候你可以认为半平面交不存在,面积为0。

需要比较熟练的运用叉积来计算方向,交点和面积。

另外这个专题还极容易炸精。。。体验极差。

半平面交的应用?

一方面是从定义上直接使用。

另一方面是它可以求出多边形的核,即多边形中可以看见多边形所有位置的点的集合。

再一方面就是,因为直线是二元一次方程的解,半平面则是二元一次不等式的解集。于是半平面交就是二元一次不等式的解集。

 

赛车:

$Description:$

这里有一辆赛车比赛正在进行,赛场上一共有N辆车,分别称为个g1,g2……gn。赛道是一条无限长的直线。最初,gi位于距离起跑线前进ki的位置。比赛开始后,车辆gi将会以vi单位每秒的恒定速度行驶。在这个比赛过程中,如果一辆赛车曾经处于领跑位置的话(即没有其他的赛车跑在他的前面),这辆赛车最后就可以得奖,而且比赛过程中不用担心相撞的问题。现在给出所有赛车的起始位置和速度,你的任务就是算出那些赛车将会得奖。

刚从凸包转半平面交时做的题不明所以。

大概可以理解为维护直线的凸包。。。

而且还不封闭所以只需要单向加边。。。和单调栈一模一样。就是凸包了啊。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 struct L{int k,b,o;friend bool operator<(L a,L b){return a.k<b.k||(a.k==b.k&&a.b<b.b);}}l[10001];
 4 vector<int>v[10001];
 5 int s[10001],tp,an,ans[10001];
 6 int main(){
 7     int n;scanf("%d",&n);
 8     for(int i=1;i<=n;++i)scanf("%d",&l[i].b),l[i].o=i;
 9     for(int i=1;i<=n;++i)scanf("%d",&l[i].k),v[i].push_back(i);
10     sort(l+1,l+1+n);
11     for(int i=1;i<=n;++i){
12         while(tp&&l[s[tp]].k==l[i].k&&l[s[tp]].b<l[i].b)tp--;
13         while(tp&&l[s[tp]].b<l[i].b)tp--;
14         if(l[s[tp]].k==l[i].k&&l[s[tp]].b==l[i].b){v[l[s[tp]].o].push_back(l[i].o);continue;}
15         while(tp>1&&1ll*(l[s[tp-1]].b-l[i].b)*(l[s[tp]].k-l[i].k)<1ll*(l[s[tp]].b-l[i].b)*(l[s[tp-1]].k-l[i].k))tp--;
16         s[++tp]=i;
17     }for(int i=1;i<=tp;++i)for(int j=0;j<v[l[s[i]].o].size();++j)ans[++an]=v[l[s[i]].o][j];
18     sort(ans+1,ans+an+1);
19     printf("%d\n",an);
20     for(int i=1;i<an;++i)printf("%d ",ans[i]);if(an)printf("%d",ans[an]);
21 }
View Code

 

暸望塔:

$Description:$

致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。我们将H村抽象为一维的轮廓。如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长希望建造的塔高度尽可能小。请你写一个程序,帮助dadzhi村长计算塔的最小高度。

比较模板。但是图形仍然不封闭,所以还差不多可以当成凸包做。

在天上维护一个半平面交,那么天地都是一个分段一次函数,最优解当然在两者的拐点之一。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const double eps=1e-8;
 4 struct P{double x,y;P(){} P(double a,double b):x(a),y(b){}}rp[301],RP[301];
 5 struct L{P s,t;double A;}r[301],R[301];
 6 P operator+(P a,P b){return P(a.x+b.x,a.y+b.y);}
 7 P operator-(P a,P b){return P(a.x-b.x,a.y-b.y);}
 8 double X(P a,P b){return a.x*b.y-a.y*b.x;}
 9 double operator*(P a,P b){return a.x*b.x+a.y*b.y;}
10 P operator*(P a,double r){return P(a.x*r,a.y*r);}
11 P v(L x){return x.t-x.s;}
12 bool cmp(L a,L b){return fabs(a.A-b.A)>eps?a.A<b.A:X(a.s-b.s,b.t-b.s)<eps;}
13 P is(L a,L b){return a.s+v(a)*(X(v(b),(a.s-b.s))/X(v(a),v(b)));}
14 int s[301],t,lc,pc;double ans=3e18;
15 void cal(P p,L l){ans=min(ans,fabs(p.y-l.s.y-v(l).y*((p.x-l.s.x)/(v(l).x))));}
16 int main(){//freopen("1.in","r",stdin);
17     int n;cin>>n;
18     for(int i=1;i<=n;++i)scanf("%lf",&rp[i].x);
19     for(int i=1;i<=n;++i)scanf("%lf",&rp[i].y);
20     for(int i=1;i<n;++i)r[i]=(L){rp[i],rp[i+1],atan2(rp[i+1].y-rp[i].y,rp[i+1].x-rp[i].x)};
21     sort(r+1,r+n,cmp);R[lc=1]=r[1];
22     for(int i=2;i<n;++i)if(fabs(r[i].A-R[lc].A)>eps)R[++lc]=r[i];
23     for(int i=1;i<=lc;++i){
24         while(t>1&&X(v(R[i]),(is(R[s[t-1]],R[s[t]])-R[i].s))<-eps)t--;
25         s[++t]=i;
26     }for(int i=1;i<t;++i)RP[++pc]=is(R[s[i]],R[s[i+1]]),R[s[i+1]].s=R[s[i]].t=RP[pc];
27     R[s[1]].s=R[s[1]].t-v(R[s[1]])*1e9; R[s[t]].t=R[s[t]].s+v(R[s[t]])*1e9;
28     for(int i=1;i<=n;++i)for(int j=1;j<=t;++j)if(R[s[j]].s.x<rp[i].x+eps&&rp[i].x<R[s[j]].t.x+eps)cal(rp[i],R[s[j]]);
29     for(int i=1;i<t;++i)for(int j=1;j<n;++j)if(r[j].s.x<RP[i].x+eps&&RP[i].x<r[j].t.x+eps)cal(RP[i],r[j]);
30     printf("%.3lf\n",ans);
31 }
有点卡精

 

射箭:

$Description:$

沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴。沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),以任意大小的力量射出带有穿透能力的光之箭。由于游戏中没有空气阻力,并且光之箭没有箭身,箭的轨迹会是一条标准的抛物线,被轨迹穿过的所有靶子都认为被沫沫射中了,包括那些 只有端点被射中的靶子。这个游戏有多种模式,其中沫沫最喜欢的是闯关模式。在闯关模式中,第一关只有一个靶 子,射中这个靶子即可进入第二关,这时在第一关的基础上会出现另外一个靶子,若能够一箭 双雕射中这两个靶子便可进入第三关,这时会出现第三个靶子。依此类推,每过一关都会新出 现一个靶子,在第 K 关必须一箭射中前 K 关出现的所有 K 个靶子才能进入第 K+1 关,否则游戏 结束。沫沫花了很多时间在这个游戏上,却最多只能玩到第七关“七星连珠”,这让她非常困惑。 于是她设法获得了每一关出现的靶子的位置,想让你告诉她,最多能通过多少关。$n \le 100000$

二次函数。不会做。发现它一定经过原点,于是所有坐标除以$x$。变为一次函数。

和《猜数游戏》很像,「满足的条件数」这个单调性很明显,不难想到二分答案。

然后其实就是若干个二元一次方程求解集是否为空。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 211111
 4 #define db long double
 5 const db eps=1e-17,inf=1e18;
 6 struct P{db x,y;P(){} P(db a,db b):x(a),y(b){}};
 7 struct L{P s,t;db A;int o;}li[S],f[S];
 8 P operator+(P a,P b){return P(a.x+b.x,a.y+b.y);}
 9 P operator-(P a,P b){return P(a.x-b.x,a.y-b.y);}
10 P operator*(P a,db b){return P(a.x*b,a.y*b);}
11 db operator*(P a,P b){return a.x*b.x+a.y*b.y;}
12 db operator^(P a,P b){return a.x*b.y-a.y*b.x;}
13 bool cmp(L a,L b){return fabs(a.A-b.A)>eps?a.A<b.A:((a.s-b.s)^(b.t-b.s))<0;}
14 P v(L a){return a.t-a.s;}
15 P is(L a,L b){return a.s+v(a)*((v(b)^(a.s-b.s))/(v(a)^v(b)));}
16 int n,q[S],h,t,lc;db x[S],yd[S],yu[S];
17 bool sat(L a,L b,L x){return ((v(x))^(is(a,b)-x.s))<-eps;}
18 bool chk(int M){h=1;t=0;
19     for(int i=1;i<=lc;++i)if(f[i].o<=M){
20         while(t>h&&sat(f[q[t]],f[q[t-1]],f[i]))t--;
21         while(t>h&&sat(f[q[h]],f[q[h+1]],f[i]))h++;
22         q[++t]=i;
23     }
24     while(t>h&&sat(f[q[t]],f[q[t-1]],f[q[h]]))t--;
25     while(t>h&&sat(f[q[h]],f[q[h+1]],f[q[t]]))h++;
26     return t>h+1;
27 }
28 int main(){
29     cin>>n;for(int i=1;i<=n;++i)scanf("%Lf%Lf%LF",&x[i],&yd[i],&yu[i]),yd[i]/=x[i],yu[i]/=x[i];
30     int m=4,l=1,r=n,ans;
31     li[1]=(L){P(eps,0),P(eps,1),0,0};
32     li[2]=(L){P(0,eps),P(1,eps),0,0};
33     li[3]=(L){P(-inf,0),P(inf,-1),0,0};
34     li[4]=(L){P(0,inf),P(-1,inf),0,0};
35     for(int i=1;i<=n;++i)
36         li[++m]=(L){P(0,1.0L*yd[i]),P(1,1.0L*yd[i]-x[i]),0,i},
37         li[++m]=(L){P(1,1.0L*yu[i]-x[i]),P(0,1.0L*yu[i]),0,i};
38     for(int i=1;i<=m;++i)li[i].A=atan2(li[i].t.y-li[i].s.y,li[i].t.x-li[i].s.x);
39     sort(li+1,li+1+m,cmp);f[lc=1]=li[1];
40     for(int i=2;i<=m;++i)if(fabs(li[i].A-f[lc].A)>eps)f[++lc]=li[i];
41     while(l<=r)if(chk(l+r>>1))l=ans=l+r>>1,l++;else r=(l+r>>1)-1;
42     cout<<ans<<endl;
43 }
超级卡精,洛谷数据更强

 

铁人双项比赛:

$Description:$

铁人双项比赛是吉林教育学院的一项传统体育项目。该项目比赛由长跑和骑自行车组成,参赛选手必须先完成k公里的长跑,然后完成r公里的骑车,才能到达终点。每个参赛选手所擅长的项目不同,有的擅长长跑,有的擅长骑车。如果总赛程s=k+r一定,那么K越大,对擅长长跑的选手越有利;k越小,对擅长骑车的选手越有利。

现在给定总赛程s,以及每个选手长跑和骑车的平均速度,请你求出对于某个指定的选手最有利的k和r。所谓最有利,是指选择了这个k和r后,该选手可以获得冠军,且领先第2名尽量地多。$n \le 100$

如果这道题你当成半平面交做那么下边这题就会很恶心吧。。。

经典的三分题目啊。。。三分长跑在总赛程中的比例就行。。。别忘了单位换算

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n;long double x[102],y[102],s,l=0,r,md;
 4 long double cal(long double t){long double mn=1e18L;
 5     for(int i=1;i<n;++i)mn=min(mn,t/x[i]+(s-t)/y[i]);
 6     return mn-t/x[n]-(s-t)/y[n];
 7 }
 8 int main(){
 9     cin>>s>>n;for(int i=1;i<=n;++i)cin>>x[i]>>y[i];
10     r=s;while(md=(l+r)/2,r-l>1e-7L)if(cal(md+1e-8L)>cal(md)+1e-15L)l=md;else r=md;
11     if(cal(l)<0)puts("NO");else printf("%.2Lf %.2Lf %.0Lf\n",l,s-l,cal(l)*3600);
12 }
View Code

 

Saber VS Lancer:

$Description:$

铁人三项是一种运动项目,和字面意思一样,是让铁做的人(?)去做三个项目,必须连续完成,而且全程讲求速度。第一项是游泳,第二项是骑自行车,第三项是跑步。现在所有选手的三个项目的速度都是已知的。但是这次比赛中,裁判可以任意选择每一个项目的路程长度(假设没有一项长度为0)。但是这样显然会影响比赛排名……有时她会按某种方式选择,使得一些个别的选手能赢得竞赛。$n\le 100$

这道题才轮的到半平面交出场吧。设总路径长度为1。

设其中两项分别为$x$,$y$,那么剩下一个就是$1-x-y$。根据这个就可以列出每个选手用时的表达式。

$n$很小,可以对每个选手单独考虑。将每个选手与其它选手比较列出若干不等式,形式很半平面交,查看是否有解即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define db long double
 4 const db eps=1e-18L;
 5 db v1[111],v2[111],v3[111];int n,m,q[111],h,t,c;
 6 struct P{db x,y; P(){} P(db a,db b):x(a),y(b){}};
 7 struct L{P s,t;db A;}li[111],r[111];
 8 P operator+(P a,P b){return P(a.x+b.x,a.y+b.y);}
 9 P operator-(P a,P b){return P(a.x-b.x,a.y-b.y);}
10 P operator*(P a,db b){return P(a.x*b,a.y*b);}
11 db operator*(P a,P b){return a.x*b.y-a.y*b.x;}
12 P v(L x){return x.t-x.s;}
13 bool cmp(L a,L b){return fabs(a.A-b.A)>eps?a.A<b.A:(a.s-b.s)*v(b)<0;}
14 P is(L a,L b){return a.s+v(a)*((v(b)*(a.s-b.s))/(v(a)*v(b)));}
15 bool sat(L a,L b,L x){return t>h&&v(x)*(is(a,b)-x.s)<eps;}
16 int main(){//freopen("triath24.in","r",stdin);freopen("my.out","w",stdout);
17     cin>>n;
18     for(int i=1;i<=n;++i)cin>>v1[i]>>v2[i]>>v3[i],v1[i]=1/v1[i],v2[i]=1/v2[i],v3[i]=1/v3[i];
19     for(int i=1;i<=n;++i){
20         m=3;h=2;t=1;li[1]={P(0,0),P(0,-1),0};li[2]={P(0,0),P(1,0),0};li[3]={P(1,0),P(0,1),0};
21         for(int j=1;j<=n;++j)if(i!=j&&fabs(v1[i]-v1[j])+fabs(v2[i]-v2[j])+fabs(v3[i]-v3[j])<eps){puts("No");goto X;}
22         for(int j=1;j<=n;++j)if(i!=j&&v1[i]>v1[j]&&v2[i]>v2[j]&&v3[i]>v3[j]){puts("No");goto X;}
23         for(int j=1;j<=n;++j)if(i!=j){
24             db s1=v1[i]-v3[i]+v3[j]-v1[j],s2=v2[i]-v3[i]-v2[j]+v3[j];
25             if(fabs(s2)<eps)li[++m]=(L){P((v3[j]-v3[i])/s1,0),P((v3[j]-v3[i])/s1,s1>eps?1:-1),0};
26             else if(s2>0)li[++m]=(L){P(1,(v1[j]-v1[i])/s2),P(0,(v3[j]-v3[i])/s2),0};
27             else li[++m]=(L){P(0,(v3[j]-v3[i])/s2),P(1,(v1[j]-v1[i])/s2),0};
28         }
29         for(int j=1;j<=m;++j)li[j].A=atan2(li[j].t.y-li[j].s.y,li[j].t.x-li[j].s.x);
30         sort(li+1,li+1+m,cmp);r[c=1]=li[1];//cout<<li[1].s.x<<' '<<li[1].s.y<<' '<<li[1].t.x<<' '<<li[1].t.y<<' '<<li[1].A<<endl;
31         for(int j=2;j<=m;++j)if(fabs(li[j].A-li[j-1].A)>eps)r[++c]=li[j];//else printf("%.10Lf %.10Lf\n",li[j].A,r[c].A);
32         for(int j=1;j<=c;++j){
33             while(sat(r[q[t]],r[q[t-1]],r[j]))t--;
34             while(sat(r[q[h]],r[q[h+1]],r[j]))h++;
35             q[++t]=j;
36         }
37         while(sat(r[q[t]],r[q[t-1]],r[q[h]]))t--;
38         while(sat(r[q[h]],r[q[h+1]],r[q[t]]))h++;
39         puts(t>h+1?"Yes":"No");X:;//cout<<i<<' '<<c<<endl;
40     }
41 }
很卡精。使得某NC:找到解决办法啦~~~~float幺二八~~~~爷再做一个计算几何爷就倒立吃x。

 

小凸想跑步:

$Description:$

小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏。

操场是个凸n边形,个顶点按照逆时针从0编号。现在小凸随机站在操场中的某个位置,标记为P点。将P点与顶点各连一条边,形成n个三角形。

如果这时P点,0号点,1号点形成的三角形的面积是n个三角形中最小的一个,小凸则认为这是一次正确站位。

现在小凸想知道他一次站位正确的概率是多少。

概率?肯定是不存在的。是面积比。

原多边形的面积拆成三角形叉积算一算就出来了。

然后要求这个三角形面积比其它的小。。。线段是确定的,三角形的面积是与横纵坐标相关的不等式。。。

又是解不等式组。。。三角形的面积直接用底乘高表示,其实不是很麻烦。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 222222
 4 #define db long double
 5 const db eps=1e-8L;
 6 int n,m,lc,q[S],h=1,t;db x[S],y[S],tA,aA;
 7 struct P{db x,y;P(){} P(db a,db b):x(a),y(b){}};
 8 struct L{P s,t;db A;}li[S],r[S];
 9 P operator+(P a,P b){return P(a.x+b.x,a.y+b.y);}
10 P operator-(P a,P b){return P(a.x-b.x,a.y-b.y);}
11 P operator*(P a,db k){return P(a.x*k,a.y*k);}
12 db operator*(P a,P b){return a.x*b.y-a.y*b.x;}
13 P v(L x){return x.t-x.s;}
14 bool cmp(L a,L b){return fabs(a.A-b.A)>eps?a.A<b.A:(a.s-b.s)*v(b)<0;}
15 P is(L a,L b){return a.s+v(a)*(((a.s-b.s)*v(b))/(v(b)*v(a)));}
16 bool sat(int a,int b,int x){return t>h&&v(r[x])*(is(r[a],r[b])-r[x].s)<eps;}
17 db cal(P x,L l){return fabs((l.s-x)*v(l));}
18 void pt(L l){cout<<'('<<l.s.x<<','<<l.s.y<<") ("<<l.t.x<<','<<l.t.y<<")\n";}
19 int main(){//freopen("1.in","r",stdin);
20     scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%Lf%Lf",&x[i],&y[i]);
21     x[n+1]=x[1];y[n+1]=y[1];
22     for(int i=1;i<=n;++i)li[++m]=(L){P(x[i],y[i]),P(x[i+1],y[i+1]),0};
23     for(int i=2;i<n;++i)aA+=cal(P(x[1],y[1]),li[i]);
24     db A=y[1]-y[2],B=x[2]-x[1],C=x[1]*y[2]-x[2]*y[1];
25     for(int i=2;i<=n;++i){
26         db x1=x[i],y1=y[i],x2=x[i+1],y2=y[i+1],a=y1-y2,b=x2-x1,c=x1*y2-x2*y1,sa=A-a,sb=B-b,sc=c-C,ea=sc/sa,eb=sc/sb;
27         //Ax+By+C<ax+by+c (A-a)x+(B-b)y<c-C sa*x+sb*y<sc
28         if(fabs(sa)+fabs(sb)<eps&&sc>0)continue;
29         if(fabs(sa)+fabs(sb)<eps)return puts("0.0000"),0;
30         if(fabs(sb)<eps&&sa<0)li[++m]=(L){P(ea,0),P(ea,-1),0};
31         else if(fabs(sb)<eps)li[++m]=(L){P(ea,0),P(ea,1),0};
32         else if(sb<0)li[++m]=(L){P(0,eb),P(1,(sc-sa)/sb),0};
33         else li[++m]=(L){P(1,(sc-sa)/sb),P(0,eb),0};
34     }
35     for(int i=1;i<=m;++i)li[i].A=atan2(li[i].t.y-li[i].s.y,li[i].t.x-li[i].s.x);
36     sort(li+1,li+m+1,cmp); r[lc=1]=li[1];
37     for(int i=2;i<=m;++i)if(li[i].A-li[i-1].A>eps)r[++lc]=li[i];//,pt(li[i]);else cout<<"del:",pt(li[i]);
38     for(int i=1;i<=lc;++i){ while(sat(q[t-1],q[t],i))t--; while(sat(q[h],q[h+1],i))h++; q[++t]=i; }
39     while(sat(q[t-1],q[t],q[h]))t--; while(sat(q[h],q[h+1],q[t]))h++;
40 //    cout<<h<<' '<<t<<endl;for(int i=h;i<=t;++i)pt(r[q[i]]);puts("---");
41     for(int i=h;i<t;++i)r[q[i]].t=r[q[i+1]].s=is(r[q[i]],r[q[i+1]]);
42 //    for(int i=h;i<=t;++i)pt(r[q[i]]);
43     P X=is(r[q[h]],r[q[t]]);
44     for(int i=2;i<t;++i)tA+=cal(X,r[q[i]]);
45     printf("%.4Lf\n",tA/aA);//cout<<h<<' '<<t<<' '<<lc<<endl;
46 }
View Code
posted @ 2020-01-13 19:10  DeepinC  阅读(314)  评论(0编辑  收藏  举报