[思路来自已发布的大佬%% 进行了具体的解释]
题目要求交点的个数我们画个图来看一下
图片说明

那么我们可以发现当我们固定某一点为起点在圆周上进行枚举

两条线有交点的条件是 两条线的端点是插着的

就用上面那个图以圆在X负半轴的交点为起点 黄色和绿色线的端点是插着的 而黄色与红色的没有 所以你就可以发现我刚刚说的是有道理的

所以我们就可以用树状数组(其他你喜欢的也可以)进行维护

之前在通过推道公式求出两个端点的坐标就可以了 (代码中有详细过程)

然后就是化曲为直 我是先将X上下半轴分开 再分别按从小到大和从大到小排

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<cmath>
#define LL long long
using namespace std;
inline void read(LL &x){
    x=0;LL f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();    }
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x*=f;
}
struct node{
    double x,y;
    LL num;
}e[100050];
LL n;
double a,b,c,d;
inline double Y(double X)
{
    return (double)(-a*X-c)*1.0/b;
}
inline LL cmpy(node a,node b)
{
    return a.y < b.y;
}
inline LL cmpu(node a,node b)
{
    return a.x>b.x;
}
inline LL cmpd(node a,node b)
{
    return a.x<b.x;
}
inline LL lowbite(LL k)
{
    return k&(-k);
}
LL N;
LL tr[100500];
inline void add(LL pos,LL v)
{
    for(LL i=pos;i<=N;i+=lowbite(i))
    {
        tr[i]+=v;
    }
}
inline LL query(LL pos)
{
    LL res=0;
    for(LL i=pos;i>0;i-=lowbite(i))
    {
        res+=tr[i];
    }
    return res;
}
LL v[50010];
int main()
{
    read(n);
    scanf("%lf",&d);
    N=n*2;
    LL k=0;
    for(LL TT=1;TT<=n;TT++)
    {
        scanf("%lf",&a);
        scanf("%lf",&b);
        scanf("%lf",&c);
        //ax+by+c=0;
        //x*x+y*y=d*d
        //y=(-ax-c)/b
        //x*x+(ax*ax+2*a*c*x+c*c)/b*b = d*d
        //x*x*b*b + a*a*x*x + 2*a*c*x = d*d*b*b - c*c
        //(a*a+b*b)*x*x + 2*a*c*x = d*d*b*b - c*c
        //(a*a+b*b)*x*x + 2*a*c*x +(c*c - d*d*b*b) = 0
        if(a==0)
        {
            //by+c=0
            e[TT*2-1].y = (-c)*1.0/b;
            e[TT*2-1].x = sqrt(d*d-e[TT*2-1].y);
            e[TT*2-1].num = TT;
            e[TT*2].y = (-c)*1.0/b;
            e[TT*2].num = TT;
            e[TT*2].x = -sqrt(d*d-e[TT*2].y);
            if(e[TT*2-1].y < 0) k++;
            if(e[TT*2].y < 0) k++;
            continue;
        }
        if(b==0)
        {
            //ax+c=0 
            e[TT*2-1].x = (-c)*1.0/a;
            e[TT*2-1].y = sqrt(d*d-e[TT*2-1].x);
            e[TT*2-1].num = TT;

            e[TT*2].x = (-c)*1.0/a;
            e[TT*2].y = -sqrt(d*d-e[TT*2].x);
            e[TT*2].num = TT;
            if(e[TT*2-1].y < 0) k++;
            if(e[TT*2].y < 0) k++;
            continue;
        }
        LL A=a*a+b*b;
        LL B=2*a*c;
        LL C=c*c-d*d*b*b;
        LL delta=B*B-4*A*C;
        if(delta<=0) continue;

        e[TT*2-1].x = (-B+sqrt(delta))*1.0/(2*A);
        e[TT*2-1].y = Y(e[TT*2-1].x);
        e[TT*2-1].num = TT; 

        if(e[TT*2-1].y < 0) k++;

        e[TT*2].x = (-B-sqrt(delta))*1.0/(2*A);
        e[TT*2].y = Y(e[TT*2].x);
        e[TT*2].num = TT; 

        if(e[TT*2].y < 0) k++;
    }
    stable_sort(e+1,e+1+n*2,cmpy);

//    for(LL i=1;i<=N;i++)
//    cout<<e[i].x<<"  "<<e[i].y <<" " <<e[i].num<<" Y1Y"<<endl;
//    cout<<endl;

    stable_sort(e+1,e+1+k,cmpd);

//    for(LL i=1;i<=N;i++)
//    cout<<e[i].x<<"  "<<e[i].y <<" " <<e[i].num<<" Y2Y"<<endl;
//    cout<<endl;

    stable_sort(e+1+k,e+1+n*2,cmpu);
    LL ans=0;
    for(LL i=1;i<=N;i++)
    {
//        cout<<e[i].x<<"  "<<e[i].y <<" " <<e[i].num<<" YYY"<<endl;
        if(e[i].num == 0) continue;
//        cout<<v[e[i].num]<<endl;
        if(v[e[i].num]==0)
        {
            v[e[i].num]=i;
            add(i,1);
        }
        else
        {
//            cout<<query(i)<<" "<<query(v[e[i].num])<<endl;
            LL tmp=(query(i)-query(v[e[i].num]));
            add(v[e[i].num],-1);
//            if(tmp<=0) continue;
            ans+=tmp;
        }
    }
    printf("%lld",ans);
    return 0;
}