NKOJ-Unknow 直线的交点

直线的交点
问题描述

伦伦刚刚在高中学习了解析几何,学会了计算两条直线的交点。这天,老师给她布置了
一道作业。在平面上有 n 条直线,他们之间有若干交点。给定一对平板(两条平行的直线),
问这有多少对直线,他们的交点在这一对平板之间(注意 (i, j) 和 (j, i) 只算一对)。

输入格式

第一行三个整数 k,a,b 表示平板的两条平行直线的方程为 y=kx+a 和 y=kx+b,保证 a<b。
第二行一个整数 n。
接下来 n 行每行两个整数 ki,bi 表示第 i 条直线的方程 y=kix+bi。

输出格式

一个整数,表示有多少对直线,他们的交点在平板之间。

样例输入

0 3 50
5
1 0
2 0
-1 0
-2 0
-1 10

样例输出

3

样例解释

只有 y=-x+10 这条直线和 y=x,y=2x,y=-2x 这三条直线的交点在区域内。

数据范围与约定

对于 30% 的数据, n≤5000。
对于 100% 的数据, n≤100000。
为了简单起见,输入数据保证,没有直线和平板平行,没有两条直线的交点在平板上。

像这种题就是给我做我都不会做

题解

简化题意

由于上下两条线是平行的
所以 可以自己画两条平行线
然后按照和上平行线的交点的顺序 一条一条加进去

然后你就会惊奇地发现

每加入一条直线 新增的交点数=之前的直线数量-加入的直线与下平行线交点的左边的交点数

没什么好证明的 观察就能知道是为什么

假设直线LA与上平行线的交点在LB与上平行线的交点左边
那么
    LB与下平行线的交点在LA与下平行线的交点的左边时 就有交点
    反之则没有

而上面 之前的直线数量-加入的直线与下平行线交点的左边的交点数 即为 与下平行线的交点在多少条直线的左边
注意 我们是按照和上平行线交点的顺序依次加入直线的 所以上述证明过程绝对可以普遍化

于是 这就变成了一道简单的树状数组题

解法

求出每条直线和上下平行线的交点 并按照和上平行线的交点的顺序排序

对于每条直线

统计 之前的直线数量-当前直线与下平行线交点的左边的交点数 ->加入结果
将当前的下交点加入树状数组

而下交点的位置需要离散化 即 它的新位置由 排序后 lower_bound求出的位置 表示

总结

这道题目其实很简单 关键在于对交点数判定的简化过程(其实这个过程中考好像就有涉及)
其次的操作是离散化 这个操作很普遍 需要熟练掌握

附上对拍代码(比题解麻烦很多,最好不要看)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define mid (l+r>>1)
#define lowbit(x) (x&-x)
using namespace std;

inline int input()
{
    char c=getchar();int o;bool f=0;
    while(c>57||c<48)f|=(c=='-'),c=getchar();
    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
    return f?-o:o;
}

struct li{double k,b;}line[101234],nline,up,down;
struct cut{double a,b;int mk;}pots[101234];
bool cp(cut a,cut b){return a.a<b.a;}

double dx,pot[201234];
int all=0,ap,ll,rr,p;
int shang[201234],xia[201234];

void add(cut a)
{
    for(int x=a.a;x<=ap;x+=lowbit(x))shang[x]++;
    for(int x=a.b;x<=ap;x+=lowbit(x))xia[x]++;
}
int sum(cut a)
{
    int ansA=0,ansB=0;
    for(int x=a.a;x;x-=lowbit(x))ansA+=shang[x];
    for(int x=a.b;x;x-=lowbit(x))ansB+=xia[x];
    return abs(ansA-ansB);
}

double jiao(li a,li b){return (b.b-a.b)/(a.k-b.k);}

void addp(li a)
{
    pots[++all].a=jiao(a,down);
    pots[all].b=jiao(a,up)+dx;
}

int main()
{
    freopen("point.in","r",stdin);
    freopen("point.out","w",stdout);
    int n;long long res=0;
    double k;
    k=down.k=up.k=input();down.b=input();up.b=input();
    dx=(up.b-down.b)*k/(1+k*k);
    n=input();
    for(int i=1;i<=n;i++)nline.k=input(),nline.b=input(),addp(nline);
    for(int i=1;i<=n;i++)pot[i<<1]=pots[i].b,pot[(i<<1)-1]=pots[i].a;
    ap=all<<1;
    sort(pot+1,pot+ap+1);
    for(int i=1;i<=n;i++)
    {
        pots[i].a=lower_bound(pot+1,pot+ap+1,pots[i].a)-pot;
        pots[i].b=lower_bound(pot+1,pot+ap+1,pots[i].b)-pot;
    }
    sort(pots+1,pots+all+1,cp);
    for(int i=1;i<=all;i++)
    {
        res+=sum(pots[i]);
        add(pots[i]);
    }
    printf("%lld",res);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值