计蒜客NOIP模拟赛 直线的交点

来源:互联网 发布:ubuntu 调整分辨率 编辑:程序博客网 时间:2024/06/05 11:53

题面


O(N2的暴力就不说了,直接进入正解。

既然题目中只求在区域内的交点个数,不妨只考虑区域内的情况。

由于题目说明了给出的直线不与区域边界直线平行,那么每条直线必然与边界直线各有一个交点。只把区域内的情况画出来应该像下图的样子。

这里写图片描述

从这里可以明显看出,如果把每条直线与左右边界的交点分别记作(axi,ayi)(bxi,byi),那么两条直线在区域内相交的充要条件是:ax1<ax2bx1>bx2。那么,如果我们按ax排序后从小到大讨论,统计答案时只需要知道逆序对个数即可。可以离散化之后上树状数组,也可以直接归并排序。下面的代码采用的是树状数组。

求出左右交点的时间复杂度为O(N),排序的时间复杂度O(NlogN)。如果通过树状数组求逆序对,时间复杂度O(NlogN)。总时间复杂度是O(NlogN)级别的。


#include<stdio.h>#include<algorithm>#define ll long long#define MAXN 100005#define dd doubleusing 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);}