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

题面


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 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值