题目链接

(BZOJ) http://lydsy.com/JudgeOnline/problem.php?id=4814
(Luogu) https://www.luogu.org/problem/P3699

题解

写了这么多扫描线依然不会写。。

首先思路非常简单,枚举每个点,把所有的直线按照极角序排序,然后扫描线解决。(注意这里扫描线是一条从这个点出发的射线)
事件有三种: (1)插入一条线段。(2)删除一条线段。(3)查询某个位置与该点的连线是否被某一目前存在的直线穿过。
显然可以用一个set维护直线,set的比较函数定义为比较这个点与当前射线的交点和当前枚举点的距离。
细节处理: (1)对于一个三角形只有与当前枚举点的线段夹角最大的两个点之间的连边有用。(2)如果遇到跨过极角序分界点(\(-\pi\)\(\pi\)),显然可以拆成两条线段。
精度问题:由于set判相等会有精度误差,因此尽量不用find(), 可以记下来每条直线插入到set中的位置。另外除了求交点距离之外的部分全都可以不用double实现。
常数问题: 注意一定不能在排序比较函数里调用三角函数!血淋淋的教训……
时间复杂度\(O(n^2\log n)\).

代码

#include<bits/stdc++.h>
#define llong long long
using namespace std;
 
inline int read()
{
    int x=0; bool f=1; int c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    if(f) return x;
    return -x;
}
 
const int N = 1000;
const double PI = acos(-1);
const double EPS = 1e-8;
inline int dcmp(double x) {return x<-EPS?-1:(x>EPS?1:0);}
struct Point
{
    llong x,y;
    Point() {}
    Point(llong _x,llong _y) {x = _x,y = _y;}
    inline double ang() const {return atan2(y,x);}
};
typedef Point Vector;
inline Point operator +(const Point &x,const Point &y) {return Point(x.x+y.x,x.y+y.y);}
inline Point operator -(const Point &x,const Point &y) {return Point(x.x-y.x,x.y-y.y);}
inline llong Dot(const Point &x,const Point &y) {return x.x*y.x+x.y*y.y;}
inline llong Cross(const Point &x,const Point &y) {return x.x*y.y-x.y*y.x;}
inline llong EuclidDist2(const Point &x,const Point &y) {return (x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y);}
inline bool operator <(const Point &x,const Point &y) {return Cross(x,y)>0;}
struct Line
{
    Point x,y;
    Line() {}
    Line(const Point &_x,const Point &_y) {x = _x,y = _y;}
};
struct Triangle
{
    Point a[3];
};
struct Query
{
    int opt,id; Point x; double ang;
} qr[N*5+3];
Triangle b[N+3];
Point a[N+3];
Line stk[N+3];
int n,m,q;
 
Vector l;
inline bool cmp_qr(const Query &x,const Query &y) {int flg = dcmp(x.ang-y.ang); return flg<0 || (flg==0 && x.opt<y.opt);}
inline double calcdis(const Line &x)
{
    double t = (double)Cross(l,x.x)/((double)Cross(x.y-x.x,l));
    double rx = x.x.x+(x.y.x-x.x.x)*t,ry = x.x.y+(x.y.y-x.x.y)*t;
    return rx*rx+ry*ry;
}
struct Element
{
    Line x;
    Element() {}
    Element(Line _x) {x = _x;}
    inline bool operator <(const Element &arg) const
    {
        double dis1 = calcdis(x),dis2 = calcdis(arg.x);
        return dcmp(dis1-dis2)<0;
    }
};
multiset<Element> s;
multiset<Element>::iterator adr[N+3];
 
int main()
{
    scanf("%d%d",&n,&m);int ans = 0;
    for(int i=1; i<=n; i++) scanf("%lld%lld",&a[i].x,&a[i].y);
    for(int i=1; i<=m; i++) scanf("%lld%lld%lld%lld%lld%lld",&b[i].a[0].x,&b[i].a[0].y,&b[i].a[1].x,&b[i].a[1].y,&b[i].a[2].x,&b[i].a[2].y);
    for(int i=1; i<=n; i++)
    {
        q = 0; l = Vector(-1,0); Line cur;
        for(int j=1; j<=m; j++)
        {
            Point t[3];
            for(int k=0; k<3; k++) t[k] = b[j].a[k]-a[i];
            sort(t,t+3),stk[j].x = t[0],stk[j].y = t[2];
            if(stk[j].x.y>=0 && stk[j].y.y<=0)
            {
                adr[j] = s.insert(Element(stk[j]));
                q++; qr[q].x = stk[j].y; qr[q].id = j; qr[q].opt = 3;
                q++; qr[q].x = stk[j].x; qr[q].id = j; qr[q].opt = 1;
            }
            else
            {
                q++; qr[q].x = stk[j].x; qr[q].id = j; qr[q].opt = 1;
                q++; qr[q].x = stk[j].y; qr[q].id = j; qr[q].opt = 3;
            }
        }
        for(int j=i+1; j<=n; j++) q++,qr[q].x = a[j]-a[i],qr[q].opt = 2;
        for(int j=1; j<=q; j++) qr[j].ang = qr[j].x.ang();
        sort(qr+1,qr+q+1,cmp_qr);
        for(int j=1; j<=q; j++)
        {
            l = qr[j].x;
            if(qr[j].opt==1)
            {
                adr[qr[j].id] = s.insert(Element(stk[qr[j].id]));
            }
            else if(qr[j].opt==3)
            {
                s.erase(adr[qr[j].id]);
            }
            else if(qr[j].opt==2)
            {
                if(s.empty()) ans++;
                else
                {
                    Line mini = (*s.begin()).x;
                    double dis1 = calcdis(mini),dis2 = qr[j].x.x*qr[j].x.x+qr[j].x.y*qr[j].x.y;
                    if(dcmp(dis1-dis2)>0) ans++;
                }
            }
        }
        s.clear();
    }
    printf("%d\n",ans);
    return 0;
}