题解 P5428 【[USACO19OPEN]Cow Steeplechase II】
这道题一看可知是要求出相连的的最前面两个,因为最多3个连在一起,甚至有时候3个也不行,所以求出相连两条即可退出。
首先想到n^{2}复杂度做法,但明显超时,需要优化,所以采取扫描线进行优化至nlog(n)。
现将每个点(注意,分开了,但要标记属于那条线)按照x轴从小到大排序,x坐标相同的按y坐标从大到小。用set进行存储,
在左端点出现后,让这条线进入集合,与两边的线进行检查是否相交,注意,set有排序功能,所以你要重载小于,要注意比较方式,求出相邻两条边在同一x坐标y坐标大小,因为这个x坐标会随扫描线移动。
在右端点出现后,让这条线出集合,因为扫描线已经扫过了。这是还要检查一遍左右两条线是否相交
,因为左右可能变化了。
注:这道题需熟练用于set的各种函数。
比较也需要技巧:对立叉乘实验
p1和p2是两条线段,且左端点处于同一位置(0,0)右端点分别为(x1,y1),(x2,y2),
线段p1和p2的叉乘结果为p1Xp2=x1y2-x2y1
如若结果大于0,则p2在p1的顺时针180度以内,如果小于零,在逆时针180度以内。
要判断是否交叉,可以取p1线上一端点连上p2线的两个端点,得到两条条线,再分别与p1进行叉乘实验。
再可以取p2线上一端点连上p1线的两个端点,得到两条条线,再分别与p2进行叉乘实验。
就可以知道是否p1两点在p2两边,p2两点在p1两边。
就能得到答案了 上代码:
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
#define LL long long
#define M 100005
void read(LL &x){
x=0;LL f=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-f;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
x*=f;
return ;
}
double x;
LL n,ans1,ans2;
struct edge{
LL x,y,id;
bool operator <(edge b)const{
return x==b.x?y<b.y:x<b.x;
}
}t[M<<1];
struct node{
edge a,b;
LL id;
bool operator ==(node k)const{
return id==k.id;
}
}p[M];
double eval(node op){
if(op.a.x==op.b.x)
return op.a.y;
return op.a.y+(op.b.y-op.a.y)*(x-op.a.x)/(op.b.x-op.a.x);
}
bool operator <(node u,node v){
return u.id!=v.id&&eval(u)<eval(v);
}
set<node>s;
LL sum[M];
LL sign(LL x){
if(!x)
return 0ll;
if(x>0)
return +1ll;
return -1ll;
}
LL operator* (edge u,edge v){
return sign(u.x*v.y-u.y*v.x);
}
edge operator- (edge u,edge v){
edge k1={u.x-v.x,u.y-v.y};
return k1;
}
bool cmp(node u,node v){
edge &a1=u.a,&a2=v.a,&b1=u.b,&b2=v.b;
return ((b2-a1) * (b1-a1)) * ((b1-a1) * (a2-a1))>=0&&((b1-a2) * (b2-a2)) * ((b2-a2) * (a1-a2))>=0;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(p[i].a.x),read(p[i].a.y),read(p[i].b.x),read(p[i].b.y);
t[(i<<1)-1]=p[i].a;
t[i<<1]=p[i].b;
p[i].a.id=p[i].b.id=p[i].id=t[(i<<1)-1].id=t[i<<1].id=i;
}
sort(t+1,t+1+(n<<1));
for(int i=1;i<=n<<1;i++){
set<node>::iterator it;
LL k=t[i].id;
ans1=k;
x=t[i].x;
it=s.find(p[k]);
if(it!=s.end()){
set<node>::iterator a_it=it,b_it=it;b_it++;
if(a_it!=s.begin()&&b_it!=s.end()){
a_it--;
if(cmp(p[a_it->id],p[b_it->id])){
ans1=a_it->id,ans2=b_it->id;
break;
}
}
s.erase(it);
}else{
set<node>::iterator new_it=s.lower_bound(p[k]);
if(new_it!=s.end()&&cmp(p[new_it->id],p[k])){
ans2=new_it->id;
break;
}
if(new_it!=s.begin()){
new_it--;
if(cmp(p[new_it->id],p[k])){
ans2=new_it->id;
break;
}
}
s.insert(p[k]);
}
}
LL ans=0;
if(ans1>ans2)
swap(ans1,ans2);
for(int i=1;i<=n;i++)
if(p[i].id!=ans2&&cmp(p[i],p[ans2]))
ans++;
printf("%lld",ans>1?ans2:ans1);
return 0;
}