NKOJ P4251 「NOIP模拟」 直线的交点【树状数组】【离散化】

这道题我考试的时候又智障了…考试的时候我想到了离散化+树状数组,想到了逆序处理交点,但是就是没有想到逆序对…所以谁都不知道做题打比赛的时候会犯一些什么神奇的错误,只能平时多加练习,形成一种好的做题习惯,这样才能在关键的时候去避免它…

由于我们只需要考虑直线与直线的交点是否在平板内部,而这样的直线与直线的交点也就相当于这两条直线被平板所截得的两条线段的交点,而这两条线段都是由直线分别与平板的两条直线的交点的 x x x坐标决定( y y y坐标可以根据 x x x解析求得),所以我们先考虑把这些 x x x坐标求出来,不妨设两条直线为:
y = k 1 x + b 1 , y = k 2 x + b 2 y=k_1x+b_1,y=k_2x+b2 y=k1x+b1y=k2x+b2
我们联立方程就解得:
x = b 2 − b 1 k 1 − k 2 x=\dfrac{b_2-b_1}{k_1-k_2} x=k1k2b2b1
所以现在我们得到了这些横坐标有什么用呢?设每条直线与平板上面一条直线的交点的横坐标为 x 1 x_1 x1,与平板下面一条直线的交点的横坐标为 x 2 x_2 x2,那么我们可以将这些线段按照 x 1 x_1 x1从小到大排序,这样我们就将这些线段按照我们的意图有序化了。那么我们这么操作有什么用呢?现在来考虑一下交点,继续假设两条线段与平板上面一条直线的交点为 x 1 , x 2 x_1,x_2 x1,x2,(假设 x 1 < x 2 x_1<x_2 x1<x2)与平板下面一条直线的交点为 X 1 , X 2 X_1,X_2 X1,X2,那么他们有交点的条件就是 X 2 < X 1 X_2<X_1 X2<X1。于是我们就可以将第一次排序后的线段按照 1 − > n 1->n 1>n标号,这个时候我们就先忽略 x x x,只考虑线段的编号与 X X X,我们再按照 X X X的大小从小到大编号,这个时候我们再来考虑两条编号为 i , j i,j i,j的线段,那么此时有交点的条件为 j < i j<i j<i,也就相当于是两个数 i , j i,j i,j的逆序关系,于是我们最终得到解法:

1. 1. 1.求出所有的 x , X x,X x,X。时间复杂度: O ( n ) O(n) O(n)

2. 2. 2.按照 x x x从小到大排序并编号。时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

3. 3. 3.按照 X X X从小到大排序并求出此时编号的逆序对数。 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

总的时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

参考代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
#define LowBit(X) (X&(-X))
using namespace std;
const LL Max=1e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
struct Node{
	LL K,B,ID;
	DB X1,X2;
}G[Max];
LL K,A,B,N,Ans,Bit[Max];
inline LL Read(){
	LL X=0;char CH=getchar();bool F=0;
	while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
	while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
	return F?-X:X;
}
inline void Write(LL X){
	if(X<0)X=-X,putchar('-');
	if(X>9)Write(X/10);
	putchar(X%10+48);
}
bool Cmp1(Node P,Node Q){
	return P.X1<Q.X1;
}
bool Cmp2(Node P,Node Q){
	return P.X2<Q.X2;
}
void Update(LL X,LL Y){
	for(;X<=N;X+=LowBit(X)){
		Bit[X]+=Y;
	}
}
LL GetSum(LL X){
	LL Sum=0;
	for(;X;X-=LowBit(X)){
		Sum+=Bit[X];
	}
	return Sum;
}
int main(){
	LL I,J,K;
	K=Read(),A=Read(),B=Read();
	N=Read();
	for(I=1;I<=N;I++){
		G[I].K=Read(),G[I].B=Read();
		G[I].X1=(G[I].B*1.0-B*1.0)/(K*1.0-G[I].K*1.0);
		G[I].X2=(G[I].B*1.0-A*1.0)/(K*1.0-G[I].K*1.0);
	}sort(G+1,G+1+N,Cmp1);
	for(I=1;I<=N;I++){
		G[I].ID=I;
	}sort(G+1,G+1+N,Cmp2);
	for(I=1;I<=N;I++){
		Ans+=(GetSum(N)-GetSum(G[I].ID));Update(G[I].ID,1);
	}
	Write(Ans);
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值