先计算几何求每条线在圆上的交点。
然后将两个交点按极角序对应到[l,r]一段区间。那么答案就是求有多少相交区间。按左端点排序离线树状数组扫一遍就行了。
#include <bits/stdc++.h>
using namespace std;
#define N 51000
double d;
int n,top,ans,cnt;
int tr[N<<1];
double st[N<<1];
struct poi
{
double x,y;
poi(){}
poi(double x,double y):x(x),y(y){}
friend poi operator+(poi p1,poi p2)
{return poi(p1.x+p2.x,p1.y+p2.y);}
friend poi operator-(poi p1,poi p2)
{return poi(p1.x-p2.x,p1.y-p2.y);}
friend double operator*(poi p1,poi p2)
{return p1.x*p2.x+p1.y*p2.y;}
friend double operator^(poi p1,poi p2)
{return p1.x*p2.y-p2.x*p1.y;}
friend poi operator*(double k,poi p1)
{return poi(p1.x*k,p1.y*k);}
friend bool operator < (const poi &p1,const poi &p2)
{return p1.x<p2.x;}
double dis(){return x*x+y*y;}
double alp(){return atan2(y,x);}
void print(){printf("%lf %lf\n",x,y);}
}p[N];
struct line
{
poi p,v;
line(){}
line(poi p,poi v):p(p),v(v){}
}l[N],l1;
poi intersect(line l1,line l2)
{
poi u=l1.p-l2.p;
double tmp=(u^l2.v)/(l2.v^l1.v);
return l1.p+tmp*l1.v;
}
poi move(poi p1,poi dir,double dis)
{return p1+(dis/sqrt(dir.dis()))*dir;}
int get(double x){return lower_bound(st+1,st+1+top,x)-st;}
void insert(int x,int v)
{
for(int i=x;i<=top;i+=i&-i)
tr[i]+=v;
}
int query(int x)
{
int ret=1;
for(int i=x;i;i-=i&-i)
ret+=tr[i];
return ret;
}
int main()
{
scanf("%d%lf",&n,&d);
for(int i=1;i<=n;i++)
{
double a,b,c;
scanf("%lf%lf%lf",&a,&b,&c);
l[i].v=poi(b,-a);
if(a)l[i].p=poi(-c/a,0);
else l[i].p=poi(0,-c/b);
l1.v=poi(a,b);
poi p1=intersect(l[i],l1);
if(p1.dis()>d*d)continue;
double dis=sqrt(d*d-p1.dis());
p[++cnt].x=move(p1,l[i].v,dis).alp();
p[cnt].y=move(p1,l[i].v,-dis).alp();
if(p[cnt].x>p[cnt].y)swap(p[cnt].x,p[cnt].y);
st[++top]=p[cnt].x;st[++top]=p[cnt].y;
}
sort(p+1,p+1+cnt);
sort(st+1,st+1+top);
for(int i=1;i<=cnt;i++)
{
ans+=query(get(p[i].y))-query(get(p[i].x)-1);
insert(get(p[i].y),1);
}
printf("%d\n",ans);
return 0;
}