O(N2) 的暴力就不说了,直接进入正解。
既然题目中只求在区域内的交点个数,不妨只考虑区域内的情况。
由于题目说明了给出的直线不与区域边界直线平行,那么每条直线必然与边界直线各有一个交点。只把区域内的情况画出来应该像下图的样子。
从这里可以明显看出,如果把每条直线与左右边界的交点分别记作 (axi,ayi) 和 (bxi,byi) ,那么两条直线在区域内相交的充要条件是: ax1<ax2且bx1>bx2 。那么,如果我们按 ax 排序后从小到大讨论,统计答案时只需要知道逆序对个数即可。可以离散化之后上树状数组,也可以直接归并排序。下面的代码采用的是树状数组。
求出左右交点的时间复杂度为 O(N) ,排序的时间复杂度 O(NlogN) 。如果通过树状数组求逆序对,时间复杂度 O(NlogN) 。总时间复杂度是 O(NlogN) 级别的。
#include<stdio.h>
#include<algorithm>
#define ll long long
#define MAXN 100005
#define dd double
using namespace std;
inline ll _R()
{
char s=getchar();ll v=0,sign=0;
while((s!='-')&&(s>57||s<48))s=getchar();
if(s=='-')sign=1,s =getchar();
for(;s>47&&s<58;s=getchar())v=v*10+s-48;
if(sign)v=-v;
return v;
}
ll K,A,B,N,k[MAXN],b[MAXN],Ans;
struct node{dd L,R;}line[MAXN];
bool operator<(node a,node b)
{
return a.L<b.L;
}
dd Hash[MAXN];
ll C[MAXN];
ll GetSum(ll x)
{
ll i,sum=0;
for(i=x;i;i^=(i&-i))sum+=C[i];
return sum;
}
void Modify(ll x)
{
ll i;
for(i=x;i<=N;i+=(i&-i))C[i]++;
}
int main()
{
int i,j;
dd tmp;
K=_R();A=_R();B=_R();
N=_R();
for(i=1;i<=N;i++)k[i]=_R(),b[i]=_R();
for(i=1;i<=N;i++)
{
tmp=1.0*(B-b[i])/(k[i]-K);
line[i].L=tmp;
tmp=1.0*(A-b[i])/(k[i]-K);
line[i].R=tmp;
Hash[i]=tmp;
}
sort(line+1,line+N+1);
sort(Hash+1,Hash+N+1);
for(i=1;i<=N;i++)
{
j=lower_bound(Hash+1,Hash+N+1,line[i].R)-Hash;
Ans+=i-1-GetSum(j-1);
Modify(j);
}
printf("%lld",Ans);
}