QOJ.ac

QOJ

IDProblemSubmitterResultTimeMemoryLanguageFile sizeSubmit timeJudge time
#497289#7734. Intersection over UnionlonelywolfTL 8ms3928kbC++2317.3kb2024-07-28 22:15:362024-07-28 22:15:36

Judging History

你现在查看的是最新测评结果

  • [2024-07-28 22:15:36]
  • 评测
  • 测评结果:TL
  • 用时:8ms
  • 内存:3928kb
  • [2024-07-28 22:15:36]
  • 提交

answer

#include <bits/stdc++.h>
using namespace std;
using db = double;

mt19937 eng(time(0));

const db eps=1e-6;
const db pi=acos(-1);
int sgn(db k) {
    if (k > eps) return 1; 
    else if (k < -eps) return -1; 
    return 0;
}
// -1: < | 0: == | 1: >
int cmp(db k1, db k2) { return sgn(k1 - k2); }
// k3 in [k1, k2]
int inmid(db k1, db k2, db k3) { return sgn(k1-k3) * sgn(k2-k3) <= 0; }
// 点 (x, y)
struct point{
    db x, y;
    point operator + (const point &k1) const { return (point){k1.x+x,k1.y+y}; }
    point operator - (const point &k1) const { return (point){x-k1.x,y-k1.y}; }
    point operator * (db k1) const { return (point){x*k1,y*k1}; }
    point operator / (db k1) const { return (point){x/k1,y/k1}; }
    int operator == (const point &k1) const { return cmp(x,k1.x)==0&&cmp(y,k1.y)==0; }
    // 逆时针旋转 k1 弧度 
    point rotate(db k1) {return (point){x*cos(k1)-y*sin(k1),x*sin(k1)+y*cos(k1)};}
    // 逆时针旋转 90°
    point rotleft() { return (point){-y,x}; }
    // 优先比较 x 坐标
    bool operator < (const point k1) const {
        int a=cmp(x,k1.x);
        if (a==-1) return 1; else if (a==1) return 0; else return cmp(y,k1.y)==-1;
    }
    // 模长
    db abs() { return sqrt(x*x+y*y); }
    // 模长的平方
    db abs2() { return x*x+y*y; }
    // 与点 k1 的距离
    db dis(point k1) {return ((*this)-k1).abs();}
    // 化为单位向量, require: abs() > 0
    point unit() {db w=abs(); return (point){x/w,y/w};}
    // 读入
    void scan() {double k1,k2; scanf("%lf%lf",&k1,&k2); x=k1; y=k2;}
    // 输出
    void print() {printf("%.11lf %.11lf\n",x,y);}
    // 方向角 atan2(y, x)
    db getw() {return atan2(y,x);} 
    // 将向量对称到 (-pi, pi] 半平面中
    point getdel() {if (sgn(x)==-1||(sgn(x)==0&&sgn(y)==-1)) return (*this)*(-1); else return (*this);}
    // (-pi, 0] -> 0, (0, pi] -> 1
	int getP() const {return sgn(y)==1||(sgn(y)==0&&sgn(x)==-1);}
};

/* 点与线段的位置关系及交点 */

// k3 在 矩形 [k1, k2] 中
int inmid(point k1,point k2,point k3){ return inmid(k1.x,k2.x,k3.x) && inmid(k1.y,k2.y,k3.y); }
db cross(point k1,point k2) { return k1.x*k2.y-k1.y*k2.x; }
db dot(point k1,point k2) { return k1.x*k2.x+k1.y*k2.y; }
// 从 k1 转到 k2 的方向角
db rad(point k1,point k2) {return atan2(cross(k1,k2),dot(k1,k2)); }
// k1 k2 k3 逆时针 1 顺时针 -1 否则 0  
int clockwise(point k1,point k2,point k3){
    return sgn(cross(k2-k1,k3-k1));
}
// 按 (-pi, pi] 顺序进行极角排序
int cmpangle (point k1,point k2){
    return k1.getP()< k2.getP()||(k1.getP()==k2.getP()&&sgn(cross(k1,k2))>0);
}
// 点 q 在线段 k1, k2 上
int onS(point k1,point k2,point q){return inmid(k1,k2,q)&&sgn(cross(k1-q,k2-k1))==0;}
// q 到直线 k1,k2 的投影 
point proj(point k1,point k2,point q) {
    point k=k2-k1; return k1+k*(dot(q-k1,k)/k.abs2());
}
// q 关于直线 k1,k2 的镜像 
point reflect(point k1,point k2,point q) {return proj(k1,k2,q)*2-q;}
// 判断 直线 (k1, k2) 和 直线 (k3, k4) 是否相交
int checkLL(point k1,point k2,point k3,point k4){
    return cmp(cross(k3-k1,k4-k1),cross(k3-k2,k4-k2))!=0;
}
// 求直线 (k1, k2) 和 直线 (k3, k4) 的交点 
point getLL(point k1,point k2,point k3,point k4){
    db w1=cross(k1-k3,k4-k3),w2=cross(k4-k3,k2-k3); 
    return (k1*w2+k2*w1)/(w1+w2);
}
int intersect(db l1,db r1,db l2,db r2){
    if (l1>r1) swap(l1,r1); if (l2>r2) swap(l2,r2); return cmp(r1,l2)!=-1&&cmp(r2,l1)!=-1;
}
// 线段与线段相交判断(非严格相交)
int checkSS(point k1,point k2,point k3,point k4){
    return intersect(k1.x,k2.x,k3.x,k4.x)&&intersect(k1.y,k2.y,k3.y,k4.y)&&
    sgn(cross(k3-k1,k4-k1))*sgn(cross(k3-k2,k4-k2))<=0&&
    sgn(cross(k1-k3,k2-k3))*sgn(cross(k1-k4,k2-k4))<=0;
}
// 点 q 到 直线 (k1, k2) 的距离
db disLP(point k1, point k2, point q) {
    return fabs(cross(k1-q, k2-q)) / k1.dis(k2);
}
// 点 q 到 线段 (k1, k2) 的距离
db disSP(point k1,point k2,point q){
    point k3=proj(k1,k2,q);
    if (inmid(k1,k2,k3)) return q.dis(k3); else return min(q.dis(k1),q.dis(k2));
}
// 线段 (k1, k2) 到 线段 (k3, k4) 的距离
db disSS(point k1,point k2,point k3,point k4){
    if (checkSS(k1,k2,k3,k4)) return 0;
    else return min(min(disSP(k1,k2,k3),disSP(k1,k2,k4)),min(disSP(k3,k4,k1),disSP(k3,k4,k2)));
}

/* 直线与半平面交 */

// 直线 p[0] -> p[1]
struct line {
    point p[2];
    line() {}
    line(point k1, point k2) {p[0]=k1; p[1]=k2;}
    point& operator [] (int k) {return p[k];}
    // k 严格位于直线左侧 / 半平面 p[0] -> p[1]
    int include(point k){return sgn(cross(p[1]-p[0],k-p[0]))>0;}
    // 方向向量
    point dir() {return p[1]-p[0];}
    // 向左平移 d, 默认为 eps 
    line push(db d = eps) {
        point delta=(p[1]-p[0]).rotleft().unit()*d;
        return {p[0]+delta,p[1]+delta};
    }
};
// 直线与直线交点
point getLL(line k1,line k2){return getLL(k1[0],k1[1],k2[0],k2[1]);}
// 两直线平行
int parallel(line k1,line k2){return sgn(cross(k1.dir(),k2.dir()))==0;}
// 平行且同向
int sameDir(line k1,line k2){return parallel(k1,k2)&&sgn(dot(k1.dir(),k2.dir()))==1;}
// 同向则左侧优先,否则按极角排序,用于半平面交
int operator < (line k1,line k2){
    if (sameDir(k1,k2)) return k2.include(k1[0]); 
    return cmpangle(k1.dir(),k2.dir());
}
// k3 (半平面) 包含 k1,k2 的交点, 用于半平面交
int checkpos(line k1,line k2,line k3) {return k3.include(getLL(k1,k2));}
// 求半平面交, 半平面是逆时针方向, 输出按照逆时针
vector<line> getHL(vector<line> L) {
    sort(L.begin(),L.end()); deque<line> q;
    for (int i = 0; i < (int)L.size(); i++) {
        if (i&&sameDir(L[i],L[i-1])) continue;
        while (q.size()>1&&!checkpos(q[q.size()-2],q[q.size()-1],L[i])) q.pop_back();
        while (q.size()>1&&!checkpos(q[1],q[0],L[i])) q.pop_front();
        q.push_back(L[i]);
    }
    while (q.size()>2&&!checkpos(q[q.size()-2],q[q.size()-1],q[0])) q.pop_back();
    while (q.size()>2&&!checkpos(q[1],q[0],q[q.size()-1])) q.pop_front();
    vector<line>ans; for (int i=0;i<q.size();i++) ans.push_back(q[i]);
    return ans;
}
db closepoint(vector<point>&A,int l,int r){ // 最近点对 , 先要按照 x 坐标排序 
    if (r-l<=5){
        db ans=1e20;
        for (int i=l;i<=r;i++) for (int j=i+1;j<=r;j++) ans=min(ans,A[i].dis(A[j]));
        return ans;
    }
    int mid=l+r>>1; db ans=min(closepoint(A,l,mid),closepoint(A,mid+1,r));
    vector<point>B; for (int i=l;i<=r;i++) if (abs(A[i].x-A[mid].x)<=ans) B.push_back(A[i]);
    sort(B.begin(),B.end(),[](point k1,point k2){return k1.y<k2.y;});
    for (int i=0;i<B.size();i++) for (int j=i+1;j<B.size()&&B[j].y-B[i].y<ans;j++) ans=min(ans,B[i].dis(B[j]));
    return ans;
}

/* 圆基础操作 */

// 圆 (o, r)
struct circle{
    point o; db r;
    void scan(){o.scan(); scanf("%lf",&r);}
    int inside(point k){return cmp(r,o.dis(k));}
};

// 两圆位置关系(两圆公切线数量)
int checkposCC(circle k1,circle k2) {
    if (cmp(k1.r,k2.r)==-1) swap(k1,k2);
    db dis=k1.o.dis(k2.o);  int w1=cmp(dis,k1.r+k2.r),w2=cmp(dis,k1.r-k2.r);
    if (w1>0) return 4; else if (w1==0) return 3; else if (w2>0) return 2; 
    else if (w2==0) return 1; else return 0;
}
// 直线与圆交点,沿 k2->k3 方向给出, 相切给出两个 
vector<point> getCL(circle k1,point k2,point k3){
    point k=proj(k2,k3,k1.o); db d=k1.r*k1.r-(k-k1.o).abs2();
    if (sgn(d)==-1) return {};
    point del=(k3-k2).unit()*sqrt(max((db)0.0,d)); return {k-del,k+del};
}
// 两圆交点,沿圆 k1 逆时针给出, 相切给出两个 
vector<point> getCC(circle k1,circle k2){
    int pd=checkposCC(k1,k2); if (pd==0||pd==4) return {};
    db a=(k2.o-k1.o).abs2(),cosA=(k1.r*k1.r+a-k2.r*k2.r)/(2*k1.r*sqrt(max(a,(db)0.0)));
    db b=k1.r*cosA,c=sqrt(max((db)0.0,k1.r*k1.r-b*b));
    point k=(k2.o-k1.o).unit(),m=k1.o+k*b,del=k.rotleft()*c;
    return {m-del,m+del};
} 
// 点到圆的切点,沿圆 k1 逆时针给出, 注意未判位置关系!! 
vector<point> TangentCP(circle k1,point k2){
    db a=(k2-k1.o).abs(), b=k1.r*k1.r/a, c=sqrt(max((db)0.0,k1.r*k1.r-b*b));
    point k=(k2-k1.o).unit(), m=k1.o+k*b, del=k.rotleft()*c;
    return {m-del, m+del};
} 
// 外公切线
vector<line> TangentoutCC(circle k1,circle k2){
    int pd=checkposCC(k1,k2); if (pd==0) return {}; 
    if (pd==1){point k=getCC(k1,k2)[0]; return {(line){k,k}};}
    if (cmp(k1.r,k2.r)==0){
        point del=(k2.o-k1.o).unit().rotleft().getdel();
        return {(line){k1.o-del*k1.r,k2.o-del*k2.r},(line){k1.o+del*k1.r,k2.o+del*k2.r}};
    } else {
        point p=(k2.o*k1.r-k1.o*k2.r)/(k1.r-k2.r);
        vector<point>A=TangentCP(k1,p),B=TangentCP(k2,p);
        vector<line>ans; for (int i=0;i<A.size();i++) ans.push_back((line){A[i],B[i]}); 
        return ans;
    }
}
// 内公切线
vector<line> TangentinCC(circle k1,circle k2){
    int pd=checkposCC(k1,k2); if (pd<=2) return {};
    if (pd==3){point k=getCC(k1,k2)[0]; return {(line){k,k}};} 
    point p=(k2.o*k1.r+k1.o*k2.r)/(k1.r+k2.r);
    vector<point>A=TangentCP(k1,p),B=TangentCP(k2,p);
    vector<line>ans; for (int i=0;i<A.size();i++) ans.push_back((line){A[i],B[i]}); 
    return ans;
}
// 所有公切线
vector<line> TangentCC(circle k1,circle k2){
    int flag=0; if (k1.r<k2.r) swap(k1,k2),flag=1;
    vector<line>A=TangentoutCC(k1,k2),B=TangentinCC(k1,k2);
    for (line k:B) A.push_back(k); 
    if (flag) for (line &k:A) swap(k[0],k[1]);
    return A;
}
// 圆 k1 与三角形 k2 k3 k1.o 的有向面积交
db getarea(circle k1,point k2,point k3){
    point k=k1.o; k1.o=k1.o-k; k2=k2-k; k3=k3-k;
    int pd1=k1.inside(k2),pd2=k1.inside(k3); 
    vector<point>A=getCL(k1,k2,k3);
    if (pd1>=0){
        if (pd2>=0) return cross(k2,k3)/2;
        return k1.r*k1.r*rad(A[1],k3)/2+cross(k2,A[1])/2;
    } else if (pd2>=0){ 
        return k1.r*k1.r*rad(k2,A[0])/2+cross(A[0],k3)/2;
    }else {
        int pd=cmp(k1.r,disSP(k2,k3,k1.o));
        if (pd<=0) return k1.r*k1.r*rad(k2,k3)/2;
        return cross(A[0],A[1])/2+k1.r*k1.r*(rad(k2,A[0])+rad(A[1],k3))/2;
    }
}
// 多边形与圆面积交
db getarea(vector<point> A, circle c) {
    int n = A.size(); 
    if(n <= 2) return 0.0;
    A.push_back(A[0]);
    db res = 0.0;
    for(int i = 0; i < n; i++) {
        point k1 = A[i], k2 = A[i+1];
        res += getarea(c, k1, k2);
    }
    return fabs(res);
}
// 三角形外接圆
circle getcircle(point k1,point k2,point k3){
    db a1=k2.x-k1.x,b1=k2.y-k1.y,c1=(a1*a1+b1*b1)/2;
    db a2=k3.x-k1.x,b2=k3.y-k1.y,c2=(a2*a2+b2*b2)/2;
    db d=a1*b2-a2*b1;
    point o=(point){k1.x+(c1*b2-c2*b1)/d,k1.y+(a1*c2-a2*c1)/d};
    return (circle){o,k1.dis(o)};
}
// 最小圆覆盖
circle getScircle(vector<point> A){
    shuffle(A.begin(), A.end(), eng);
    circle ans=(circle){A[0],0};
    for (int i=1;i<A.size();i++)
        if (ans.inside(A[i])==-1){
            ans=(circle){A[i],0};
            for (int j=0;j<i;j++)
                if (ans.inside(A[j])==-1){
                    ans.o=(A[i]+A[j])/2; ans.r=ans.o.dis(A[i]);
                    for (int k=0;k<j;k++)
                        if (ans.inside(A[k])==-1)
                            ans=getcircle(A[i],A[j],A[k]);
                }
        }
    return ans;
}


// 判断是否为逆时针凸包
int checkconvex(vector<point>A) {
    int n=A.size(); A.push_back(A[0]); A.push_back(A[1]);
    for (int i=0;i<n;i++) if (sgn(cross(A[i+1]-A[i],A[i+2]-A[i]))==-1) return 0;
    return 1;
}
// 点与简单多边形位置关系:2 内部 1 边界 0 外部
int contain(vector<point>A,point q) {
    int pd=0; A.push_back(A[0]);
    for (int i=1;i<A.size();i++){
        point u=A[i-1],v=A[i];
        if (onS(u,v,q)) return 1; if (cmp(u.y,v.y)>0) swap(u,v);
        if (cmp(u.y,q.y)>=0||cmp(v.y,q.y)<0) continue;
        if (sgn(cross(u-v,q-v))<0) pd^=1;
    }
    return pd<<1;
}
// flag=0 不严格 flag=1 严格 
vector<point> ConvexHull(vector<point> A, int flag = 1) {
    int n = A.size();
    if(n == 1) return A;
    if(n == 2) {
        if(A[0] == A[1]) return {A[0]};
        else return A;
    }
    vector<point> ans(n * 2);
    sort(A.begin(), A.end()); int now = -1;
    for(int i = 0; i < A.size(); i++) {
        while (now > 0 && sgn(cross(ans[now]-ans[now-1], A[i]-ans[now-1])) < flag) now--;
        ans[++now] = A[i];
    } int pre = now;
    for(int i = n - 2; i >= 0; i--) {
        while (now > pre && sgn(cross(ans[now]-ans[now-1], A[i]-ans[now-1])) < flag) now--;
        ans[++now] = A[i];
    }
    ans.resize(now);
    return ans;
}
// 凸包直径
db convexDiameter(vector<point>A){
    int now=0,n=A.size(); db ans=0;
    for (int i=0;i<A.size();i++){
        now=max(now,i);
        while (1){
            db k1=A[i].dis(A[now%n]),k2=A[i].dis(A[(now+1)%n]);
            ans=max(ans,max(k1,k2)); if (k2>k1) now++; else break;
        }
    }
    return ans;
}
// 直线切凸包,保留 k1,k2,p 逆时针的所有点
vector<point> convexcut(vector<point>A,point k1,point k2){
    int n=A.size(); A.push_back(A[0]); vector<point>ans;
    for (int i=0;i<n;i++){
        int w1=clockwise(k1,k2,A[i]),w2=clockwise(k1,k2,A[i+1]);
        if (w1>=0) ans.push_back(A[i]);
        if (w1*w2<0) ans.push_back(getLL(k1,k2,A[i],A[i+1]));
    }
    return ans;
}
// 多边形 A 和 直线 (线段) k1->k2 严格相交, 注释部分为线段
int checkPoS(vector<point>A,point k1,point k2) {
    struct ins{
        point m,u,v;
        int operator < (const ins& k) const {return m<k.m;}
    }; vector<ins>B;
    //if (contain(A,k1)==2||contain(A,k2)==2) return 1;
    vector<point>poly=A; A.push_back(A[0]); 
    for (int i=1;i<A.size();i++) if (checkLL(A[i-1],A[i],k1,k2)){
        point m=getLL(A[i-1],A[i],k1,k2); 
        if (inmid(A[i-1],A[i],m)/*&&inmid(k1,k2,m)*/) B.push_back((ins){m,A[i-1],A[i]});
    }
    if (B.size()==0) return 0; sort(B.begin(),B.end()); 
    int now=1; while (now<B.size()&&B[now].m==B[0].m) now++; 
    if (now==B.size()) return 0;
    int flag=contain(poly,(B[0].m+B[now].m)/2);
    if (flag==2) return 1;
    point d=B[now].m-B[0].m;
    for (int i=now;i<B.size();i++){
        if (!(B[i].m==B[i-1].m)&&flag==2) return 1;
        int tag=sgn(cross(B[i].v-B[i].u,B[i].m+d-B[i].u));
        if (B[i].m==B[i].u||B[i].m==B[i].v) flag+=tag; else flag+=tag*2;
    }
    //return 0;
    return flag==2;
}
int checkinp(point r,point l,point m){
	if (cmpangle(l,r)){return cmpangle(l,m)&&cmpangle(m,r);}
	return cmpangle(l,m)||cmpangle(m,r);
}
// 快速检查线段是否和多边形严格相交
int checkPosFast(vector<point>A,point k1,point k2){
	if (contain(A,k1)==2||contain(A,k2)==2) return 1; if (k1==k2) return 0;
	A.push_back(A[0]); A.push_back(A[1]);
	for (int i=1;i+1<A.size();i++)
		if (checkLL(A[i-1],A[i],k1,k2)){
			point now=getLL(A[i-1],A[i],k1,k2);
			if (inmid(A[i-1],A[i],now)==0||inmid(k1,k2,now)==0) continue;
			if (now==A[i]){
				if (A[i]==k2) continue;
				point pre=A[i-1],ne=A[i+1];
				if (checkinp(pre-now,ne-now,k2-now)) return 1;
			} else if (now==k1){
				if (k1==A[i-1]||k1==A[i]) continue;
				if (checkinp(A[i-1]-k1,A[i]-k1,k2-k1)) return 1;
			} else if (now==k2||now==A[i-1]) continue;
			else return 1;
		}
	return 0;
}


db area(auto &A) {
    db ans = 0;
    for (int i = 0; i < A.size(); i++) {
        ans += cross(A[i], A[(i + 1) % A.size()]);
    }
    if (ans < 0) {
        ans = -ans;
    }
    return ans / 2;
}

void solve() {
    vector<point> P(4);
	for (auto &[x, y] : P) {
		cin >> x >> y;
	}

    if (cross(P[1] - P[0], P[2] - P[0]) < 0) {
        reverse(P.begin(), P.end());
    }

    if (P[0].x == P[1].x || P[0].y == P[1].y) {
        cout << 1 << "\n";
        return;
    }

    auto o = (P[0] + P[2]) / 2;
    for (auto &p : P) {
        p = p - o;
        p = p.unit();
    }

    db mx = 0, my = 0;
    for (int i = 0; i < 4; i++) {
        mx = max(mx, P[i].x);
        my = max(my, P[i].y);
    }

  	db s = area(P);

	vector<line> L(8);
	for (int i = 0; i < 4; i++) {
		L[i] = {P[i], P[(i + 1) % 4]};
	}

    auto calc2 = [&] (db l, db d) {
        L[4] = line(point{0, d}, point{-1, d});
        L[5] = line(point{0, -d}, point{1, -d});
        L[6] = line(point{l, 0}, point{l, 1});
        L[7] = line(point{-l, 0}, point{-l, -1});

        auto v = getHL(L);

        vector<point> a(v.size());
        for (int i = 0; i < v.size(); i++) {
            a[i] = getLL(v[i], v[(i + 1) % v.size()]);
        }

        db s1 = area(a);
        db s2 = l * d * 4;
        return s1 / (s + s2 - s1);
    };

  	auto calc = [&] (db d) -> db {
        db lo1 = 0, hi1 = mx;
  		while (hi1 - lo1 > eps) {
  			db lm = (2 * lo1 + hi1) / 3;
  			db rm = (lo1 + 2 * hi1) / 3;
  			if (calc2(lm, d) < calc2(rm, d)) {
  				lo1 = lm;
  			} else {
  				hi1 = rm;
  			}
  		}
  		return calc2(lo1, d);
  	};	

  	db lo = 0, hi = my;
    
  	while (hi - lo > eps) {
  		db lm = (2 * lo + hi) / 3;
  		db rm = (lo + 2 * hi) / 3;
  		if (calc(lm) < calc(rm)) {
  			lo = lm;
  		} else {
  			hi = rm;
  		}
  	} 
  	cout << calc(lo) << "\n";
}

signed main () {  
    ios::sync_with_stdio(false);
    cin.tie(nullptr);  

    cout << fixed << setprecision(10);
    int t;
    cin >> t;
    while (t--) {
    	solve();
    }

    return 0;
}  
  

Details

Tip: Click on the bar to expand more detailed information

Test #1:

score: 100
Accepted
time: 8ms
memory: 3928kb

input:

3
0 2 2 0 0 -2 -2 0
7 -2 9 -2 9 2 7 2
7 13 11 10 5 2 1 5

output:

0.7071067812
1
0.6238432248

result:

ok 3 numbers

Test #2:

score: -100
Time Limit Exceeded

input:

10000
-568767734 379152673 -565681345 -54946093 -131582579 -51859704 -134668968 382239062
-194884120 -559906233 -194884142 -158042604 -998611400 -158042648 -998611378 -559906277
459335966 -945199065 478030260 -934243779 450535683 -887326546 431841389 -898281832
-483567491 491964356 -523827401 408140...

output:


result: