这道题我考试的时候又智障了……考试的时候我想到了离散化+树状数组,想到了逆序处理交点,但是就是没有想到逆序对……所以谁都不知道做题打比赛的时候会犯一些什么神奇的错误,只能平时多加练习,形成一种好的做题习惯,这样才能在关键的时候去避免它……
由于我们只需要考虑直线与直线的交点是否在平板内部,而这样的直线与直线的交点也就相当于这两条直线被平板所截得的两条线段的交点,而这两条线段都是由直线分别与平板的两条直线的交点的
坐标决定(
坐标可以根据
解析求得),所以我们先考虑把这些
坐标求出来,不妨设两条直线为:
我们联立方程就解得:
所以现在我们得到了这些横坐标有什么用呢?设每条直线与平板上面一条直线的交点的横坐标为 ,与平板下面一条直线的交点的横坐标为 ,那么我们可以将这些线段按照 从小到大排序,这样我们就将这些线段按照我们的意图有序化了。那么我们这么操作有什么用呢?现在来考虑一下交点,继续假设两条线段与平板上面一条直线的交点为 ,(假设 )与平板下面一条直线的交点为 ,那么他们有交点的条件就是 。于是我们就可以将第一次排序后的线段按照 标号,这个时候我们就先忽略 ,只考虑线段的编号与 ,我们再按照 的大小从小到大编号,这个时候我们再来考虑两条编号为 的线段,那么此时有交点的条件为 ,也就相当于是两个数 的逆序关系,于是我们最终得到解法:
求出所有的 。时间复杂度:
按照 从小到大排序并编号。时间复杂度:
按照 从小到大排序并求出此时编号的逆序对数。
总的时间复杂度:
参考代码:
#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;
}