JDFZOJ2204:打地鼠 期望+凸包

思路:
首先\(O(n^3)\)的思路非常朴素,只需要考虑每条向量的贡献即可.向量的出现概率即为这两个点出现且向量右侧的点均不出现.于是我们需要暴力\(O(n)\)check右侧的点是否均不出现.

我们考虑以一个点为起点的所有向量的答案.

那么对于每一个向量右侧的点必定都是一段区间,且满足单调性.

 

我们枚举起点,然后把剩下的点按照关于起点的极角序排序,然后依次扫描即可.

当然有几个坑点:

概率有一些为0的不能直接乘除,而需要记录零的个数.

还有就是坑爹的三点共线问题.

 

我们考虑当角度相同时,按照距离从大到小排序.

然后扫描的时候进行一些特判.这里没太弄明白就先挖坑了.

(好像一开始先把点随机扰动一下应该也可以吧)

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
 
typedef double f2;
 
#define N 1010
struct Point{
    f2 x,y,p;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
    Point operator+(const Point&B)const{return Point(x+B.x,y+B.y);}
    Point operator-(const Point&B)const{return Point(B.x-x,B.y-y);}
    Point operator*(const f2&p)const{return Point(p*x,p*y);}
}P[N];
inline f2 sqr(const f2&x){return x*x;}
inline f2 cross(const Point&a,const Point&b){return a.x*b.y-a.y*b.x;}
inline f2 dot(const Point&a,const Point&b){return a.x*b.x+a.y*b.y;}
inline f2 length(const Point&a){return sqrt(sqr(a.x)+sqr(a.y));}
 
static const f2 eps=1e-8;
inline int dcmp(const f2&x){return fabs(x)<eps?0:(x<0?-1:1);}
 
struct Seg{
    Point v;
    f2 Ang,area,p,len;
    inline bool operator<(const Seg&B)const{
        return dcmp(Ang-B.Ang)==0?len>B.len:Ang<B.Ang;
    }
}S[N];
 
int main(){
    int n;scanf("%d",&n);register int i,j,k;
    for(i=0;i<n;++i)scanf("%lf%lf%lf",&P[i].x,&P[i].y,&P[i].p),P[i].p/=1000.0;
    f2 ans=0;
    for(i=0;i<n;++i){
        int id=0;
        for(j=0;j<n;++j)if(i^j){
            S[id].v=P[i]-P[j];
            S[id].Ang=atan2(S[id].v.y,S[id].v.x);
            S[id].area=cross(P[i],P[j]);
            S[id].p=P[j].p;
            S[id].len=length(S[id].v);
            id++;
        }
        sort(S,S+id);
        long double nowp=P[i].p;
        int zero=0;
        for(j=0;j<id;++j)if(dcmp(S[j].p-1)==0)++zero;else nowp*=(1-S[j].p);
         
        k=0;
        for(j=0;j<id;++j){
            while(dcmp(cross(S[j].v,S[k].v))>0||(dcmp(cross(S[j].v,S[k].v))==0&&dcmp(dot(S[k].v,S[j].v-S[k].v))<=0)){
                if(dcmp(1-S[k].p))nowp/=1-S[k].p;else --zero;
                k=(k+1)%id;
                if(k==j)break;
            }
            if(!zero)ans+=S[j].area*nowp*S[j].p;
            if(dcmp(1-S[j].p)!=0)nowp*=(1-S[j].p);else ++zero;
        }
    }
     
    printf("%.6lf",ans*.5);
    return 0;
}

BZOJ1043:[HAOI2008]下落的圆盘 计算几何+离线处理

思路:

嗯,我诅咒你,出题人,持续一辈子的呦~~

我们考虑每个圆对于最终答案的贡献,显然就是将所有在之后的圆覆盖在这个圆上的部分去掉,剩下的若干段弧长.

那么接下来就是找出对于一个圆,他有哪些部分是被覆盖的.

我们对于每一个后面的圆,找出他覆盖在这个圆上面的极角序区间,最后我们再求一次区间的并就可以了.

找出这段区间成为了这道题目的难点.

首先我们找出两个圆的交点.什么你不会?

看下面的图片:(假设是圆\(O_2\)覆盖圆\(O_1\))

(看不清楚图片请点击一下放大来看)求出\(a,b\)之后,就很容易利用向量求出两个交点坐标了.(具体怎么求就不要问我了吧QoQ)

然后我们确定这两个点关于点\(O_1\)的极角序,但是究竟是由哪一个指向哪一个呢?

我们只要再确定点\(O_2\)关于圆心\(O_1\)的极角序,并保证上述的区间经过这个位置即可.

具体的细节就只能自己体会了.可以参照我的代码.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
 
typedef double f2;
inline f2 sqr(const f2&x){
    return x*x; 
}
static const f2 PI=acos(-1.0);
 
#define N 1010
struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
    Point operator+(const Point&B)const{return Point(x+B.x,y+B.y);}
    Point operator-(const Point&B)const{return Point(B.x-x,B.y-y);}
    Point operator*(const f2&p)const{return Point(p*x,p*y);}
};
f2 dist(const Point&A,const Point&B){return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));}
 
typedef Point vector;
inline vector Getvertical(vector _v){return vector(-_v.y,_v.x);}
inline f2 Getlength(vector _v){return sqrt(sqr(_v.x)+sqr(_v.y));}
inline vector Getunitvector(vector _v){
    f2 length=Getlength(_v);
    return vector(_v.x/length,_v.y/length);
}
 
struct Circle{
    Point o;f2 r;
    Circle(){}
    Circle(Point _o,f2 _r):o(_o),r(_r){}
    f2 get(const Point&B)const{return atan2(B.y-o.y,B.x-o.x);}
}C[N];
inline bool IsIntersect(const Circle&A,const Circle&B){
    return A.r+B.r>dist(A.o,B.o);
}
inline bool IsCover(const Circle&A,const Circle&B){
    return A.r>=B.r&&dist(A.o,B.o)<=A.r-B.r;
}
 
struct Interval{
    f2 l,r;
    Interval(){}
    Interval(f2 _l,f2 _r):l(_l),r(_r){}
    bool operator<(const Interval&B)const{return l<B.l;}
}S[N<<1];int id;
inline bool find(f2 l,f2 r,f2 x){
	if(l<=r)return x>=l&&x<=r;else return x>=l||x<=r;
}
inline void add(f2 l,f2 r){
    if(l<=r)S[++id]=Interval(l,r);else S[++id]=Interval(l,PI),S[++id]=Interval(-PI,r);
}
 
int main(){
    int n;scanf("%d",&n);
    register int i,j,k;
    for(i=1;i<=n;++i){
        scanf("%lf%lf%lf",&C[i].r,&C[i].o.x,&C[i].o.y);
    }
    f2 totans=0,a,b,d,ang1,ang2,ango;Point p1,p2; 
    vector v,_v;
    for(i=n;i>=1;--i){
        bool cover=0;id=0;
        for(j=i+1;j<=n&&!cover;++j)if(IsCover(C[j],C[i]))cover=1;
        if(!cover){
            for(j=i+1;j<=n;++j)if(IsIntersect(C[i],C[j])&&!IsCover(C[i],C[j])){
                d=dist(C[i].o,C[j].o);
                b=(sqr(C[i].r)-sqr(C[j].r)+sqr(d))/(2*d);
                a=sqrt(sqr(C[i].r)-sqr(b));
                v=C[i].o-C[j].o,_v=Getunitvector(Getvertical(v));
                p1=C[i].o+v*(b/d)+_v*a,p2=C[i].o+v*(b/d)+_v*(-a);
                ang1=C[i].get(p1),ang2=C[i].get(p2),ango=C[i].get(C[j].o);
                if(find(ang1,ang2,ango))add(ang1,ang2);else add(ang2,ang1);
            }
            totans+=2*PI*C[i].r;
            if(id){
                sort(S+1,S+id+1);
                f2 Maxr;
                for(j=1;j<=id;){
                    for(Maxr=S[j].r,k=j+1;k<=id;++k){
                        if(S[k].l>Maxr)break;
                        if(S[k].r>Maxr)Maxr=S[k].r;
                    }
                    totans-=C[i].r*(Maxr-S[j].l);
                    j=k;
                }
            }
        }
    }
    printf("%.3lf",totans);
    return 0;
}

Codeforces77E Martian Food 计算几何+圆的反演

思路:

设两个比较小的圆分别是A,B.

我们将最大圆以及其中一个圆A的交点取出,并以这个点为反演中心,以合适的长度为反演半径,进行反演.

容易发现大圆和A反演之后都成了一条直线.而圆B由于与这两个圆都有唯一交点,那么反演之后也一定都有唯一交点,因此只能是夹在两条直线中间.

不妨以反演中心为原点,三个圆的直径为x轴(方向任意)建立平面直角坐标系,显然B反演之后的圆纵坐标为0.

那么如果我们再放上别的圆(假设一直放在上方),由于它与这三个圆都有唯一交点,所以依然是被卡在两条直线中间,而且在刚才反演的那个圆上方.

这些圆的大小相同,因此我们可以\(O(1)\)得到第\(k\)个圆的圆心半径.

接下来只要再反演回来就好了.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

typedef double f2;

struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
}P1,P2;
f2 sqr(const f2&x){return x*x;}
f2 dis(const Point&A,const Point&B){return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));}

inline void GetPointOnDiameter(f2 ox,f2 oy,f2 r,f2 k,f2 b){
    f2 deltax=r/sqrt(k*k+1);
    P1=Point(ox-deltax,k*(ox-deltax)+b);
    P2=Point(ox+deltax,k*(ox+deltax)+b);
}

Point Invert(f2 ox,f2 oy,f2 r,Point A){
    Point o(ox,oy);f2 d=dis(o,A),_d=r*r/d;
    f2 x=ox+(A.x-ox)*(_d/d);
    f2 y=oy+(A.y-oy)*(_d/d);
    return Point(x,y);
}

int main(){
    int Testcase;cin>>Testcase;
    f2 R,r,lx,rx,invr,ox,oy,totalr;int k;
    while(Testcase--){
        scanf("%lf%lf%d",&R,&r,&k);
        invr=r/2;
        rx=invr*invr/(2*r);
        lx=invr*invr/(2*R);
        totalr=(rx-lx)/2,ox=(lx+rx)/2,oy=2*k*totalr;
        GetPointOnDiameter(ox,oy,totalr,oy/ox,0);
        printf("%.10lf\n",dis(Invert(0,0,invr,P1),Invert(0,0,invr,P2))/2);
    }return 0;
}

 

 

BZOJ3199:[Sdoi2013]escape 半平面交+最短路