牛客练习赛60 F-几何带师 题解

几何带师

https://ac.nowcoder.com/acm/contest/4853/F

题目传送门

题目大意: 给出一条线段 以及 个点,问从这 个点里面选出两个点,这两个点所在直线穿过 的方案数。

题解

考虑在 同侧的两点 ,假如 穿过 ,那么一定有 内或 内。

不妨设 内,那么一定满足 间一定满足 ,这个您随手画两条线段就能明白。

然后这个东西是个二维偏序问题,可以在 的时间内求出。

假如 异侧,那么就满足

移一下项,就是 ,依然是个二维偏序问题。

再说点细节,判断一个点在 哪一侧时,用两向量的叉积模长的正负性来判断(具体参考这里),求角度时,用三条边长度和余弦定理求出该角的 ,再用C++里的 函数求出其角度。

至于求二维偏序问题时,用的是简单的树状数组的做法。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 400010
#define ll long long

int n;
struct point{
    ll x,y;
    point operator+(const point &b){return (point){x+b.x,y+b.y};}
    point operator-(const point &b){return (point){x-b.x,y-b.y};}
    ll chaji(const point &b){return x*b.y-y*b.x;}
}P,Q,a[maxn];
double s[maxn][2],b[maxn];
double dis(point x,point y){return sqrt(1.0*(x.x-y.x)*(x.x-y.x)+1.0*(x.y-y.y)*(x.y-y.y));}
ll tree[maxn];
void add(int x){for(;x<=maxn-10;x+=(x&-x))tree[x]++;}
ll sum(int x){ll re=0;for(;x;x-=(x&-x))re+=tree[x];return re;}
double getangle(double x,double y,double z){return acos((x*x+y*y-z*z)/(2.0*x*y));}
struct par{
    int x,y,type;par(int xx=0,int yy=0,int Type=0):x(xx),y(yy),type(Type){}
    bool operator <(const par b)const{return x==b.x?(y<b.y):(x<b.x);}
}id[maxn];int t,tt;
ll work1(point &A,point &B)//同侧
{
    ll re=0;t=tt=0;memset(tree,0,sizeof(tree));
    for(int i=1;i<=n;i++)if((B-A).chaji(a[i]-A)>0)//只要一侧的点
    {
        t++;point P=a[i]; double PA=dis(P,A),PB=dis(P,B),AB=dis(A,B);
        s[t][0]=getangle(PA,AB,PB);s[t][1]=getangle(PB,AB,PA);//用余弦公式计算角度
        b[++tt]=s[t][0];b[++tt]=s[t][1];//记录下角度用于离散化
    }
    sort(b+1,b+tt+1);for(int i=1;i<=t;i++)//求二维偏序前的离散化
    id[i].x=lower_bound(b+1,b+tt+1,s[i][0])-b,id[i].y=lower_bound(b+1,b+tt+1,s[i][1])-b;
    sort(id+1,id+t+1);for(int i=1;i<=t;i++)re+=sum(id[i].y),add(id[i].y); return re;
}
const double Pi=acos(-1);
ll work2(point &A,point &B)//异侧
{
    ll re=0;tt=0;memset(tree,0,sizeof(tree));
    for(int i=1;i<=n;i++)
    {
        point P=a[i]; double PA=dis(P,A),PB=dis(P,B),AB=dis(A,B);
        s[i][0]=getangle(PA,AB,PB);s[i][1]=getangle(PB,AB,PA);
        if((B-A).chaji(P-A)>0)s[i][0]=Pi-s[i][0],s[i][1]=Pi-s[i][1];
        b[++tt]=s[i][0];b[++tt]=s[i][1];
    }
    sort(b+1,b+tt+1);for(int i=1;i<=n;i++)
    id[i].x=lower_bound(b+1,b+tt+1,s[i][0])-b,id[i].y=lower_bound(b+1,b+tt+1,s[i][1])-b,id[i].type=((B-A).chaji(a[i]-A)>0);
    sort(id+1,id+n+1);for(int i=1;i<=n;i++)if(id[i].type)re+=sum(id[i].y-1);else add(id[i].y); return re;
}

int main()
{
    scanf("%d %lld %lld %lld %lld",&n,&P.x,&P.y,&Q.x,&Q.y);
    for(int i=1;i<=n;i++)scanf("%lld %lld",&a[i].x,&a[i].y);
    printf("%lld",work1(P,Q)+work1(Q,P)+work2(P,Q));
}
全部评论

相关推荐

11-01 20:03
已编辑
门头沟学院 算法工程师
Amazarashi66:这种也是幸存者偏差了,拿不到这个价的才是大多数
点赞 评论 收藏
分享
11-18 09:44
Java
小白也想要offer:简历别放洋屁,搞不还还放错了,当然你投外企除外,以上纯属个人观点
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务