[计算几何]一些模型的积累

计算几何基本模板

//浮点误差
const double eps = 1e-8;
//点/向量
struct Vec{
    double x, y;
    Vec() {}
    Vec(double _x, double _y) : x(_x), y(_y) {}
    inline double angle() const{return atan2(y,x);}
    inline double len() const{return sqrt(x*x+y*y);}
    inline Vec l_rot() const{return Vec(-y,x);}
    inline Vec r_rot() const{return Vec(y,-x);}
};
inline Vec operator + (const Vec &a, const Vec &b){return Vec(a.x+b.x,a.y+b.y);}
inline Vec operator - (const Vec &a, const Vec &b){return Vec(a.x-b.x,a.y-b.y);}
template<typename T> inline Vec operator * (const Vec &a, T b){return Vec(a.x*b,a.y*b);}
template<typename T> inline Vec operator * (T a, const Vec &b){return Vec(a*b.x,a*b.y);}
template<typename T> inline Vec operator / (const Vec &a, T b){return Vec(a.x/b,a.y/b);}
inline double dot (const Vec &a, const Vec &b){return a.x*b.x+a.y*b.y;}
inline double cross (const Vec &a, const Vec &b){return a.x*b.y-a.y*b.x;}
typedef Vec Poi;
//直线/线段
struct Line{
    Poi op, ed; double k, b, ang;
    Line(){}
    Link(Poi A, Poi B):op(A),ed(B){
        k = (A.y-B.y)/(A.x-B.x); b = A.y-k*A.x;
        ang = atan2(k);
    }
}
//圆
struct Circle{
    Poi O; double r;
}
//浮点数比较大小
inline bool gt(double x, double y = 0.){ return x>y+eps; }
inline bool lt(double x, double y = 0.){ return x<y-eps; }
inline bool ge(double x, double y = 0.){ return x>=y-eps; }
inline bool le(double x, double y = 0.){ return x<=y+eps; }
inline bool eq(double x, double y = 0.){ return fabs(x-y)<=eps; }
//直线求交
inline Poi GetIntersection(const Line &a, const Line &b){
    double x = cross(a.ed-a.op, b.ed-b.op);
    double y = cross(b.op-a.op, a.ed-a.op);
    return b.op+(b.ed-b.op)*y/x;
}
//两圆求交
inline void GetIntersection(Circle O1, Circle O2, double dis){
    double alpha = atan2(O2.O.y-O1.O.y, O2.O.x-O1.O.x)+pi, // alpha /in (0, 2pi]
           beta = acos((O1.r*O1.r+dis*dis-O2.r*O2.r)/(2*O1.r*dis)); //beta /in (0, pi]
    pair<double, double> tmp = MP(alpha-beta, alpha+beta);
    if (ge(tmp.FT) && le(tmp.SC, 2*pi)) Interval[++tot] = tmp;
    else if (lt(tmp.FT)){ Interval[++tot] = MP(tmp.FT+2*pi, 2*pi); Interval[++tot] = MP(0, tmp.SC); }
    else if (gt(tmp.SC, 2*pi)){ Interval[++tot] = MP(tmp.FT, 2*pi); Interval[++tot] = MP(0, tmp.SC-2*pi); }
}
//极角区间并
inline double IntervalUnion(){
    double res = 0., L = -1., R = -1.;  
    sort(Interval+1, Interval+1+tot);
    rep(i, 1, tot){
        if (gt(Interval[i].FT, R)) res += R-L, L = Interval[i].FT, R = Interval[i].SC;
        else if (gt(Interval[i].SC, R)) R = Interval[i].SC;
    }
    res += R-L;
    return 2*pi-res;
}
//判断点在有向线段的左侧
inline bool OnLeft(const Line &a, const Poi &b){ 
    return le(cross(b-a.op, a.ed-a.op));
}
//三角形外心
//设a(x1,y1),b(x2,y2),c(x3,y3),外心o(x,y)
//由定义ao=bo,bo=co,得到一个二次式
//换元 Ax=2(x2-x1),Ay=2(x3-x2),
//    Bx=2(y2-y1),By=2(y3-y2),
//    Cx=x2^2+y2^2-x1^2-y1^2,Cy=x3^2+y3^2-x2^2-y2^2
//得到x=(CxBy-CyBx)/(AxBy-AyBx)
//   y=(CxAy-CyAx)/(BxAy-ByAx),是叉积的形式
inline Poi solve(Poi a, Poi b, Poi c){
    Poi A = 2*Vec(b.x-a.x, c.x-b.x);
    Poi B = 2*Vec(b.y-a.y, c.y-b.y);
    Poi C = Vec(sqr(b.len())-sqr(a.len()), sqr(c.len())-sqr(b.len()));
    double x = cross(C, B), y = cross(C, A), M = cross(A, B);
    return Vec(x,-y)/M;
}
//求凸包面积
inline double CalS(){ double ans = 0.;
    if (n < 3) return ans;
    CH[n+1] = CH[1]; rep(i, 1, n) ans += cross(CH[i], CH[i+1]);
    return fabs(ans)/2.;
}
//水平序凸包
//Andrew Scan
//O(nlogn)
inline void ConvexHull(){
    static Poi stack[MaxN]; int top = 0;    
    rep(i, 1, n){
        while (top >= 2 && ge0(cross(stack[top]-stack[top-1], p[i]-stack[top]))) top--;
        stack[++top] = p[i];
    }
    rep(i, 1, top) CH[++r] = stack[i]; top = 0;
    rep(i, 1, n){
        while (top >= 2 && le0(cross(stack[top]-stack[top-1], p[i]-stack[top]))) top--;
        stack[++top] = p[i];
    }
    per(i, top-1, 2) CH[++r] = stack[i]; top = 0;
}
//半平面交
//S&I算法
//O(nlogn)
inline void HalfPlainIntersection(){ 
    int tot = 0; sort(line+1, line+1+n);
    rep(i, 1, n){
        if (!eq(line[i].ang, line[i-1].ang)) tot++;
        line[tot] = line[i];    
    }
    int l = 1, r = 0; q[++r] = line[1]; q[++r] = line[2];
    rep(i, 3, tot){
        for (; l < r && !OnLeft(line[i], GetIntersection(q[r], q[r-1])); r--);
        for (; l < r && !OnLeft(line[i], GetIntersection(q[l], q[l+1])); l++);
        q[++r] = line[i];
    }
    for (; l < r && !OnLeft(q[l], GetIntersection(q[r], q[r-1])); r--);
    for (; l < r && !OnLeft(q[r], GetIntersection(q[l], q[l+1])); l++);
    q[r+1] = q[l];
    n = 0; rep(i, l, r) CH[++n] = GetIntersection(q[i], q[i+1]);
}
//辛普森自适应积分
inline double Simpson(double fl, double fr, double fmid, double h){ return h*(fl+4*fmid+fr)/6.0; }
inline double Integral(double l, double r, double mid, double fl, double fr, double fmid, double S){
    double lmid = (l+mid)/2.0, rmid = (mid+r)/2.0;
    double flmid = f(lmid), frmid = f(rmid);
    double lS = Simpson(fl, fmid, flmid, mid-l), rS = Simpson(fmid, fr, frmid, r-mid);
    if (eq(lS + rS, S)) return S;
    return Integral(l, mid, lmid, fl, fmid, flmid, lS)+Integral(mid, r, rmid, fmid, fr, frmid, rS);
}

点集三角形覆盖计数

给一个点集,统计覆盖了原点的三角形个数
解:
枚举点 i ,原点与i连线两侧分成两个半平面,同一个半平面内的任意两点与 i 组成的三角形均不包含原点,考虑到重复计数,只需考虑同一方向的半平面即可不重不漏,按极角序扫描可以将平方优化到线性
(BZOJ1914)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define debug(...) fprintf(stderr, __VA_ARGS__)
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

typedef long long ll;
const double eps = 1e-7;
const int N = 111111;

struct Vec{
    double x, y, angle;
    Vec() {}
    Vec(double _x, double _y) : x(_x), y(_y) {angle = atan2(y, x);}
    inline void cal_angle() {angle = atan2(y, x);}
};
inline Vec operator + (const Vec &a, const Vec &b) {return Vec(a.x+b.x, a.y+b.y);}
inline Vec operator - (const Vec &a, const Vec &b) {return Vec(a.x-b.x, a.y-b.y);}
template<typename T> inline Vec operator * (const Vec &a, T b) {return Vec(a.x*b, a.y*b);}
template<typename T> inline Vec operator * (T a, const Vec &b) {return Vec(a*b.x, a*b.y);}
inline Vec operator / (const Vec &a, double b) {return Vec(a.x/b, a.y/b);}
inline double dot(const Vec &a, const Vec &b) {return a.x*b.x + a.y*b.y;}
inline double cross(const Vec &a, const Vec &b) {return a.x*b.y - a.y*b.x;}

typedef Vec Poi;
inline bool angle_cmp(Poi a, Poi b) {return a.angle < b.angle;}
inline bool gt0(double x) {return x-eps>0;}

int n;
Poi a[N];
ll ans;

int main(){
    read(n);
    rep(i, 1, n) {read(a[i].x); read(a[i].y); a[i].cal_angle();}
    sort(a+1, a+1+n, angle_cmp);

    int last = 1, cnt = 0;
    rep(i, 1, n){
        while ((last%n+1 != i) && gt0(cross(a[i], a[last%n+1]))) last++, cnt++;
        ans += 1ll * cnt * (cnt-1) / 2;
        cnt--;
    }

    printf("%lld\n", 1ll*n*(n-1)*(n-2)/6 - ans);
    return 0;
}

点集凹四边形计数

给一个点集,统计凹四边形的数量
解:
枚举中间凹点,转化为点集三角形覆盖计数问题
(BZOJ1913)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define debug(...) fprintf(stderr, __VA_ARGS__)
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

typedef long long ll;
const int N = 5555;
struct Vec{
    double x, y, angle;
    Vec() {}
    Vec(double _x, double _y):x(_x), y(_y) {angle = atan2(y, x);}
    friend double atan2(Vec a){return atan2(a.y, a.x);}
};
inline Vec operator + (const Vec &a, const Vec &b) {return Vec(a.x+b.x, a.y+b.y);}
inline Vec operator - (const Vec &a, const Vec &b) {return Vec(a.x-b.x, a.y-b.y);}
template<typename T> inline Vec operator * (const Vec &a, T b) {return Vec(a.x*b, a.y*b);}
template<typename T> inline Vec operator * (T a, const Vec &b) {return Vec(a*b.x, a*b.y);}
inline Vec operator / (const Vec &a, double b) {return Vec(a.x/b, a.y/b);}
inline double dot(const Vec &a, const Vec &b) {return a.x*b.x + a.y*b.y;}
inline double cross(const Vec &a, const Vec &b) {return a.x*b.y - a.y*b.x;}
typedef Vec Poi;
inline bool angle_cmp(Vec a, Vec b) {return a.angle<b.angle;}

ll ans1, ans2, n;
Vec p[N], a[N];

inline ll solve(int k){
    ll tot = 1ll*(n-1)*(n-2)*(n-3)/6; 
    int len = 0;
    for (int i = 1; i <= n; i++)
        if (i != k) p[++len] = a[i]; else p[0] = a[i];
    rep(i, 1, len) p[i] = p[i]-p[0];
    sort(p+1, p+1+len, angle_cmp);
    for (int i = 1, r = 1, t = 0; i <= len; i++){
        if (r%len+1 == i) r++, t++;
        while ((r%len+1 != i) && (cross(p[i], p[r%len+1]) >= 0)) r++, t++; 
        tot -= 1ll*t*(t-1)/2;
        t--;
    }
    return tot;
}

int main(){
    read(n);
    rep(i, 1, n) read(a[i].x), read(a[i].y);

    rep(i, 1, n) ans1 += solve(i);
    ans2 = 1ll*n*(n-1)*(n-2)*(n-3)/24 - ans1;
    double ans = 2*ans2+ans1;
    ans /= 1ll*n*(n-1)*(n-2)/6; ans += 3;
    printf("%.6lf\n", ans);

    return 0;
}

点集三角形面积和

给一个点集,求所有以点集里的点为顶点的三角形的面积和
解:
固定一条边,考虑剩下的一个点,将叉积式子展开发现只要维护一个和就行了
(BZOJ1132)

最小圆覆盖

给一个点集,求一个圆使得所有点被覆盖且圆最小
解:
随机增量法,参见这里http://wenku.baidu.com/view/162699d63186bceb19e8bbe6.html
(BZOJ2823)

#include <bits/stdc++.h>

using namespace std;

const double eps = 1e-6;
const int N = 1000000+10;

#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define debug(...) fprintf(stderr, __VA_ARGS__)

struct Vec{
    double x, y;
    Vec() {}
    Vec(double _x, double _y) : x(_x), y(_y) {}
    inline double len() const{return sqrt(x*x+y*y);}
};
inline Vec operator + (const Vec &a, const Vec &b){return Vec(a.x+b.x,a.y+b.y);}
inline Vec operator - (const Vec &a, const Vec &b){return Vec(a.x-b.x,a.y-b.y);}
template<typename T> inline Vec operator * (const Vec &a, T b){return Vec(a.x*b,a.y*b);}
template<typename T> inline Vec operator * (T a, const Vec &b){return Vec(a*b.x,a*b.y);}
template<typename T> inline Vec operator / (const Vec &a, T b){return Vec(a.x/b,a.y/b);}
inline double dot (const Vec &a, const Vec &b){return a.x*b.x+a.y*b.y;}
inline double cross (const Vec &a, const Vec &b){return a.x*b.y-a.y*b.x;}
typedef Vec Poi;
inline bool gt(double x, double y) {return x-y>eps;}
inline bool eq0(double x) {return abs(x)<=eps;}
inline bool lt0(double x) {return x<-eps;}

int n;
Poi p[N], O;
double r;

inline double sqr(double x) {return x*x;}
inline Poi solve(Poi a, Poi b, Poi c){
    Poi A = 2*Vec(b.x-a.x, c.x-b.x);
    Poi B = 2*Vec(b.y-a.y, c.y-b.y);
    Poi C = Vec(sqr(b.len())-sqr(a.len()), sqr(c.len())-sqr(b.len()));
    double x = cross(C, B), y = cross(C, A), M = cross(A, B);
    return Vec(x,-y)/M;
}
int main(){
    scanf("%d", &n);
    rep(i, 1, n) scanf("%lf%lf", &p[i].x, &p[i].y);
    O = p[1]; r = 0;
    rep(i, 2, n) if (gt((p[i]-O).len(), r)){
        O = p[i]; r = 0;
        rep(j, 1, i-1) if (gt((p[j]-O).len(), r)){
            O = (p[i]+p[j])/2; r = (p[j]-O).len();
            rep(k, 1, j-1) if (gt((p[k]-O).len(), r)){
                Poi a = p[i], b = p[j], c = p[k];
                if (eq0(cross(b-a, c-b))){
                    if (lt0(dot(c-a, b-a))) a = c;
                    else if(lt0(dot(c-b, a-b))) b = c;
                    O = (a+b)/2; r = (O-a).len();
                }else{ O = solve(a, b, c); r = (O-a).len(); }
            }
        }
    }
    printf("%.2lf %.2lf %.2lf\n", O.x, O.y, r);
    return 0;
}

最小矩形覆盖

给一个点集,求一个矩形使所有点被覆盖且矩形面积最小
解:
显然矩形一定有一条边是凸包上的边,旋转卡壳枚举这条边即可
(BZOJ1815)

点集最大矩形

给一个点集,求以点集里四个点为顶点的最大矩形面积
解:
考虑两条线段,它们能成为一个矩形的两条对角线当且仅当它们长度相同且中点重合,枚举所有线段,按长度排序,相同长度按中心位置排序,扫一遍即可知道答案。
(BZOJ2338)

点集最大四边形

给一个点集,求以点集里四个点为顶点的最大四边形面积
解:
显然这些点应该都在这个点集的凸包上,我们求出凸包,枚举四边形对角线上的顶点i j ,对于同一个i,另外两个顶点随着 j 的变化是单调的,旋转卡壳即可。
(BZOJ1069)

//By Hachiman
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define REP(i, n) for (int i = 0; i < (n); i++)
#define PER(i, n) for (int i = (n)-1; ~i; i--)
#define EREP(p, x) for (Edge *p = g[x]; p; p = p->nxt)
#define NEREP(p, x) for (Ede *&p = cur[x]; p; p = p->nxt)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define FT first
#define SC second
#ifdef Hachiman
#   define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#   define debug(...)
#endif
#define RND(l, r) rand()%((r)-(l)+1)+(l)
typedef long long ll;
typedef long double ld;
typedef unsigned int ui;
typedef pair<int, int> PII;
const int INF = 0x7fffffff;
const double eps = 1e-8;
const int MOD = 1000000007;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
} 
template<typename T> inline void ckmax(T &a, T b){ if (a < b) a = b; }
template<typename T> inline void ckmin(T &a, T b){ if (a > b) a = b; }
template<typename T> inline void CKmax(T &a, T b){ if (a < b) { a = b; return 1;} return 0; }
template<typename T> inline void CKmin(T &a, T b){ if (a > b) { a = b; return 1;} return 0; }
inline bool eq(double x, double y){ return fabs(x-y) < eps; }
inline bool ge0(double x){ return x>=-eps; }
inline bool le0(double x){ return x<=eps; }
inline bool gt0(double x){ return x>-eps; }
inline bool lt0(double x){ return x<eps; }

struct Vec{
    double x, y;
    Vec(){}
    Vec(double X, double Y):x(X), y(Y){}
};
inline bool operator < (const Vec &a, const Vec &b){ if (eq(a.x, b.x)) return a.y<b.y; return a.x<b.x; }
inline Vec operator + (const Vec &a, const Vec &b){ return Vec(a.x+b.x, a.y+b.y); }
inline Vec operator - (const Vec &a, const Vec &b){ return Vec(a.x-b.x, a.y-b.y); }
template<typename T> inline Vec operator * (const Vec &a, T b){ return Vec(a.x*b, a.y*b); }
template<typename T> inline Vec operator * (T b, const Vec &a){ return Vec(a.x*b, a.y*b); }
template<typename T> inline Vec operator / (const Vec &a, T b){ return Vec(a.x/b, a.y/b); }
inline double cross(const Vec &a, const Vec &b){ return a.x*b.y-b.x*a.y; }
inline double dot(const Vec &a, const Vec &b){ return a.x*b.x+a.y*b.y; }
typedef Vec Poi;

const int MaxN = 3000;
int n, r, head; 
Poi CH[MaxN<<1], p[MaxN<<1];
double ans;

inline void ConvexHull(){
    static Poi stack[MaxN]; int top = 0;    
    rep(i, 1, n){
        while (top >= 2 && ge0(cross(stack[top]-stack[top-1], p[i]-stack[top]))) top--;
        stack[++top] = p[i];
    }
    rep(i, 1, top) CH[++r] = stack[i]; top = 0;
    rep(i, 1, n){
        while (top >= 2 && le0(cross(stack[top]-stack[top-1], p[i]-stack[top]))) top--;
        stack[++top] = p[i];
    }
    per(i, top-1, 2) CH[++r] = stack[i]; top = 0;
}
inline void RotatingCalipers(){
    Poi *p1, *p2, *p3, *p4;
    for (int i = r; i; i--, CH[++r] = CH[++head]){
        p1 = &CH[head+1]; p2 = p1+2; p3 = p1+1; p4 = p2+1;
        for (CH[r+1] = CH[head+1]; p2-CH<r; p2++){
            while (lt0(cross(*(p3+1)-*p3, *p2-*p1))) p3++;
            while (lt0(cross(*(p4+1)-*p4, *p1-*p2))) p4++;      
            ckmax(ans, cross(*p4-*p3, *p2-*p1)/2);
        }
    }
}
int main(int argc, char *argv[]){
    read(n);
    rep(i, 1, n) scanf("%lf%lf", &p[i].x, &p[i].y);
    sort(p+1, p+1+n);
    ConvexHull();
    RotatingCalipers();
    printf("%.3lf\n", ans);
    return 0;
}

圆的周长并

给一些相互覆盖的圆,求最后能看见的轮廓线总长
解:
考虑一个圆对最终答案的贡献,一个圆的可见部分必然是若干连续的极角区间。我们求出该圆与其它所有圆相交的极角区间,做一遍区间并即可
(BZOJ1043)

//By Hachiman
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MP make_pair
#define FT first
#define SC second
typedef long double ld;
const double eps = 1e-8;
const double pi = acos(-1.);
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
} 
template<typename T> inline void ckmax(T &a, T b){ if (a < b) a = b; }
template<typename T> inline void ckmin(T &a, T b){ if (a > b) a = b; }
template<typename T> inline bool CKmax(T &a, T b){ if (a < b) { a = b; return 1;} return 0; }
template<typename T> inline bool CKmin(T &a, T b){ if (a > b) { a = b; return 1;} return 0; }
inline bool gt(double x, double y = 0.){ return x-y>-eps; }
inline bool lt(double x, double y = 0.){ return x-y<eps; }
inline bool ge(double x, double y = 0.){ return x-y>=-eps; }
inline bool le(double x, double y = 0.){ return x-y<=eps; }
inline bool eq(double x, double y = 0.){ return fabs(x-y)<=eps; }

const int MaxN = 1010;
struct Vec{
    double x, y;
    Vec(){}
    Vec(double X, double Y):x(X), y(Y){}
    inline double len() const{ return sqrt(x*x+y*y); }
};
inline Vec operator - (const Vec &a, const Vec &b){ return Vec(a.x-b.x, a.y-b.y); }
inline double Distance(const Vec &a, const Vec &b){ Vec c = a-b; return c.len(); }
typedef Vec Poi;
struct Circle{ Poi O; double r; }circle[MaxN];
pair<double, double> Interval[MaxN];
int n, tot;
double ans;

inline void GetIntersection(Circle O1, Circle O2, double dis){
    double alpha = atan2(O2.O.y-O1.O.y, O2.O.x-O1.O.x)+pi, // alpha /in (0, 2pi]
           beta = acos((O1.r*O1.r+dis*dis-O2.r*O2.r)/(2*O1.r*dis)); //beta /in (0, pi]
    pair<double, double> tmp = MP(alpha-beta, alpha+beta);
    if (ge(tmp.FT) && le(tmp.SC, 2*pi)) Interval[++tot] = tmp;
    else if (lt(tmp.FT)){ Interval[++tot] = MP(tmp.FT+2*pi, 2*pi); Interval[++tot] = MP(0, tmp.SC); }
    else if (gt(tmp.SC, 2*pi)){ Interval[++tot] = MP(tmp.FT, 2*pi); Interval[++tot] = MP(0, tmp.SC-2*pi); }
}
inline double IntervalUnion(){
    double res = 0., L = -1., R = -1.;  
    sort(Interval+1, Interval+1+tot);
    rep(i, 1, tot){
        if (gt(Interval[i].FT, R)) res += R-L, L = Interval[i].FT, R = Interval[i].SC;
        else if (gt(Interval[i].SC, R)) R = Interval[i].SC;
    }
    res += R-L;
    return 2*pi-res;
}
int main(int argc, char *argv[]){
    read(n);
    per(i, n, 1) scanf("%lf%lf%lf", &circle[i].r, &circle[i].O.x, &circle[i].O.y);
    rep(i, 1, n){
        tot = 0;
        try{
            rep(j, 1, i-1){
                double dis = Distance(circle[i].O, circle[j].O);
                if (gt(circle[j].r-circle[i].r, dis)) throw(false);
                if (gt(circle[i].r+circle[j].r, dis) && lt(fabs(circle[i].r-circle[j].r), dis))
                    GetIntersection(circle[i], circle[j], dis);
            }
        }catch(bool){ continue; }
        ans += IntervalUnion()*circle[i].r;
    }
    printf("%.3lf\n", ans);
    return 0;
}

曼哈顿距离

曼哈顿距离可以这样定义:
定义1:平面上两个点(x1,y1),(x2,y2),它们的切比雪夫距离可以定义为 |x1x2|+|y1y2|
定义2:一个点与周围四连通的点的距离都为 1

切比雪夫距离

切比雪夫距离可以这样定义:
定义1:平面上两个点(x1,y1),(x2,y2),它们的切比雪夫距离可以定义为 max { |x1x2|,|y1y2| }
定义2:一个点与周围八连通的点的距离都为 1
对于一个点集,它们之间的切比雪夫距离可以通过将(x,y)变为 (x+y,xy) 来转化为曼哈顿距离的一半

三角形面积并

给一些三角形,求它们的面积并
解:
将所有线段的交点都取出来,按 x 排序并unique,所有横坐标把整个图形切成一条一条,由于相邻的交点之间不存在其它交点,因此一条中只存在一些不相交的梯形和三角形,每个竖条分别统计即可。
(BZOJ1845)

//By Hachiman
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define REP(i, n) for (int i = 0; i < (n); i++)
#define PER(i, n) for (int i = (n)-1; ~i; i--)
#define EREP(p, x) for (Edge *p = g[x]; p; p = p->nxt)
#define NEREP(p, x) for (Ede *&p = cur[x]; p; p = p->nxt)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define FT first
#define SC second
#ifdef Hachiman
#   define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#   define debug(...)
#endif
#define RND(l, r) rand()%((r)-(l)+1)+(l)
typedef long long ll;
typedef long double ld;
typedef unsigned int ui;
typedef pair<int, int> PII;
const ld INF = 1e18;
const ld eps = 1e-8;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
} 
template<typename T> inline void ckmax(T &a, T b){ if (a < b) a = b; }
template<typename T> inline void ckmin(T &a, T b){ if (a > b) a = b; }
template<typename T> inline bool CKmax(T &a, T b){ if (a < b) { a = b; return 1;} return 0; }
template<typename T> inline bool CKmin(T &a, T b){ if (a > b) { a = b; return 1;} return 0; }
inline bool gt(ld x, ld y = 0.){ return x>y+eps; }
inline bool lt(ld x, ld y = 0.){ return x<y-eps; }
inline bool ge(ld x, ld y = 0.){ return x>=y-eps; }
inline bool le(ld x, ld y = 0.){ return x<=y+eps; }
inline bool eq(ld x, ld y = 0.){ return fabs(x-y)<=eps; }

const int MaxN = 510;

struct Vec{
    ld x, y;
    Vec(){}
    Vec(ld X, ld Y):x(X), y(Y){}
    void set(){ ld X, Y; cin >> X >> Y; x = X; y = Y; }
}p[MaxN*MaxN];
inline bool operator < (const Vec &a, const Vec &b){ return lt(a.x,b.x)||(eq(a.x,b.x)&&lt(a.y,b.y)); }
inline Vec operator + (const Vec &a, const Vec &b){ return Vec(a.x+b.x, a.y+b.y); }
inline Vec operator - (const Vec &a, const Vec &b){ return Vec(a.x-b.x, a.y-b.y); }
template<typename T> inline Vec operator * (const Vec &a, T b){ return Vec(a.x*b, a.y*b); }
template<typename T> inline Vec operator * (T b, const Vec &a){ return Vec(a.x*b, a.y*b); }
template<typename T> inline Vec operator / (const Vec &a, T b){ return Vec(a.x/b, a.y/b); }
inline ld dot(const Vec &a, const Vec &b){ return a.x*b.x+a.y*b.y; }
inline ld cross(const Vec &a, const Vec &b){ return a.x*b.y-b.x*a.y; }
typedef Vec Poi;
struct Triangle{
    Poi dot[3];
    void set(){ REP(i, 3) dot[i].set(); }
}T[MaxN];
struct Line{
    Poi op, ed;
    Line(){}
    Line(Poi A, Poi B):op(A), ed(B){}
}line[MaxN][3];
int n, tot;
pair<ld, ld> Interval[MaxN];

inline bool Intersect(const Line &a, const Line &b){
    ld c1 = cross(b.ed-b.op,a.ed-b.op), c2 = cross(b.ed-b.op,a.op-b.op);
    ld c3 = cross(a.ed-a.op,b.ed-a.op), c4 = cross(a.ed-a.op,b.op-a.op);
    return lt(c1*c2)&&lt(c3*c4);
}
inline Poi GetIntersection(const Line &a, const Line &b){
    return b.op+(b.ed-b.op)*cross(b.op-a.op,a.ed-a.op)/cross(a.ed-a.op,b.ed-b.op);
}
int main(int argc, char *argv[]){
    freopen("hehe.in", "r", stdin);
    freopen("hehe.out", "w", stdout);
    read(n);
    rep(i, 1, n){
        T[i].set(); REP(j, 3) p[++tot] = T[i].dot[j];
        sort(T[i].dot, T[i].dot+3);
        if (gt(cross(T[i].dot[2]-T[i].dot[0],T[i].dot[1]-T[i].dot[0])))
            line[i][0]=Line(T[i].dot[0],T[i].dot[2]),line[i][1]=Line(T[i].dot[2],T[i].dot[1]),line[i][2]=Line(T[i].dot[1],T[i].dot[0]);
        else 
            line[i][0]=Line(T[i].dot[2],T[i].dot[0]),line[i][1]=Line(T[i].dot[0],T[i].dot[1]),line[i][2]=Line(T[i].dot[1],T[i].dot[2]);
    }
    rep(i, 1, n) rep(j, 1, i-1) REP(x, 3) REP(y, 3) 
        if (Intersect(line[i][x], line[j][y])) p[++tot] = GetIntersection(line[i][x], line[j][y]);
    sort(p+1, p+1+tot);
    ld ans = 0; 
    rep(i, 2, tot){ Line T = Line(Vec((p[i-1].x+p[i].x)/2, -INF), Vec((p[i-1].x+p[i].x)/2, INF));
        int cnt = 0;
        rep(j, 1, n) if (Intersect(T, line[j][0])){
            Poi bottom = GetIntersection(T, line[j][0]), 
                top = GetIntersection(T, line[j][Intersect(T, line[j][1])?1:2]);
            if (top < bottom) swap(top, bottom);
            Interval[++cnt] = MP(bottom.y, top.y);
        }
        ld L = -1., R = -1., sum = 0.;
        sort(Interval+1, Interval+1+cnt);
        rep(j, 1, cnt){
            if (gt(Interval[j].FT, R)) sum += R-L, L = Interval[j].FT, R = Interval[j].SC;
            else if (gt(Interval[j].SC, R)) R = Interval[j].SC;
        }
        sum += R-L;
        ans += (p[i].x-p[i-1].x)*sum;
    }
    cout<<fixed<<setprecision(2)<<ans-eps<<endl;
    return 0;
}

平面最短周长三角形

给一个点集,求以点集里的点为顶点的最小周长三角形
解:
1.记录全局答案 ans
2.将所有点按照 x 值排序
3.定义Solve(l,r)为处理 [l,r] 区间内的最小三角形
4.对于每层 Solve(l,r) ,将当前区间分成左右两部分,分别递归处理
5.两侧的最小三角形都以处理完毕,现在我们要处理的就是两区间之间的点构成的三角形
6.将本层中与点 mid 的横坐标之差不超过 ans/2 的点拎出来,按照纵坐标排序 (其实这步可以直接递归退出时归并排序)
7.维护双指针,保证内部纵坐标之差不超过 ans/2 ,对于每个点在范围内暴力查找最小三角形即可

//By Hachiman
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define REP(i, n) for (int i = 0; i < (n); i++)
#define PER(i, n) for (int i = (n)-1; ~i; i--)
#define EREP(p, x) for (Edge *p = g[x]; p; p = p->nxt)
#define NEREP(p, x) for (Ede *&p = cur[x]; p; p = p->nxt)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define FT first
#define SC second
#ifdef Hachiman
#   define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#   define debug(...)
#endif
#define RND(l, r) rand()%((r)-(l)+1)+(l)
typedef long long ll;
typedef long double ld;
typedef unsigned int ui;
typedef pair<int, int> PII;
const int INF = 0x7fffffff;
const double eps = 1e-8;
const int MOD = 1000000007;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
} 
template<typename T> inline void ckmax(T &a, T b){ if (a < b) a = b; }
template<typename T> inline void ckmin(T &a, T b){ if (a > b) a = b; }
template<typename T> inline bool CKmax(T &a, T b){ if (a < b) { a = b; return 1;} return 0; }
template<typename T> inline bool CKmin(T &a, T b){ if (a > b) { a = b; return 1;} return 0; }

const int MaxN = 200100;

struct Vec{
    int x, y;
    Vec(){}
    Vec(int X, int Y):x(X), y(Y){}
    inline void set(){ int X, Y; read(X); read(Y); x = X; y = Y; }
    inline double len() const{ return sqrt(1.0*x*x+1.0*y*y); }
}p[MaxN], P[MaxN];
inline bool operator < (const Vec &a, const Vec &b){ return a.x<b.x||(a.x==b.x&&a.y<b.y); }
inline Vec operator - (const Vec &a, const Vec &b){ return Vec(a.x-b.x, a.y-b.y); }
inline double Distance(const Vec &a, const Vec &b){ Vec c = a-b; return c.len(); }
int n;
double ans = 1e18;

inline void DivideAndConquer(int l, int r){
    if (l == r) return;
    if (l + 1 == r){ if (p[l].y > p[r].y) swap(p[l], p[r]); return; }
    int mid = l+r >> 1, T = p[mid].x;
    DivideAndConquer(l, mid); DivideAndConquer(mid+1, r);
    int l1 = l, l2 = mid+1;
    rep(i, l, r){
        if ((l2>r) || (l1<=mid&&p[l1].y<p[l2].y)) P[i] = p[l1++];
        else P[i] = p[l2++]; 
    }
    rep(i, l, r) p[i] = P[i];
    for (int i = l, tail = l; i <= r; i++) if (abs(p[i].x-T)<ans/2){
        for (; p[i].y-p[tail].y>ans/2; tail++);
        for (int j = tail; j < i-1; j++) if (abs(p[j].x-T)<ans/2)
            rep(k, j+1, i-1) ckmin(ans, Distance(p[i],p[j])+Distance(p[j],p[k])+Distance(p[k],p[i]));   
    }
}   
int main(int argc, char *argv[]){
    read(n);
    rep(i, 1, n) p[i].set();
    sort(p+1, p+1+n);
    DivideAndConquer(1, n);
    printf("%.6lf\n", ans);
    return 0;
}

To be continued…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值