版权声明:转载请声明 https://blog.csdn.net/ezoiHQM/article/details/82154368
题意:给定一个折线图,按x轴递增的顺序给出。对于每个条line,求出在它之后,且下标最小的line。输出这个下标。 其中
。
首先我们需要用线段树,每个节点维护这个节点所表示的区间的点的凸包。
查询的时候,我们可以判断当前的区间的凸包是否与原直线有交点,如果有的话就递归左子树,如果左子树的凸包存在与原直线的交点,那么就可以直接返回,否则再递归右子树判断。
快速判断一个凸包与一条直线是否有交点,我们可以考虑二分,利用向量叉积的几何意义即可。具体就看看代码吧。
如果有误在评论区吼一声哦!
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
struct Vector{
int x,y;
Vector(int a=0,int b=0):x(a),y(b){}
friend ll operator*(Vector a,Vector b){
return 1ll*a.x*b.y-1ll*b.x*a.y;
}
friend Vector operator-(Vector a,Vector b){
return Vector(a.x-b.x,a.y-b.y);
}
}a[100010];
struct Convex_hull{
vector<Vector>c;
void insert(Vector a){
for(int j=c.size();j>1&&(a-c[j-2])*(c[j-1]-c[j-2])<=0;j--)
c.pop_back();
c.push_back(a);
return;
}
bool check(Vector a,Vector b){
int l=0,r=c.size()-2;
while(l<r){
int mid=(l+r)>>1;
if((c[mid]-a)*(b-a)<(c[mid+1]-a)*(b-a))
r=mid;
else l=mid+1;
}
return (c[l]-a)*(b-a)<0||(c[l+1]-a)*(b-a)<0;
}
}tr[400010];
void build(int o,int l,int r){
if(l==r){
tr[o].insert(a[l]);
tr[o].insert(a[l+1]);
return;
}
for(int i=l;i<=r+1;i++)
tr[o].insert(a[i]);
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
return;
}
int query(int o,int l,int r,int L,int R,Vector a,Vector b){
if(L<=l&&r<=R){
if(!tr[o].check(a,b))
return 0;
if(l==r)
return l;
}
int mid=(l+r)>>1,ret=0;
if(L<=mid)
ret=query(o<<1,l,mid,L,R,a,b);
if(!ret&&R>mid)
ret=query(o<<1|1,mid+1,r,L,R,a,b);
return ret;
}
int rd(){
int x=0;
char c;
do c=getchar();
while(!isdigit(c));
do{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}while(isdigit(c));
return x;
}
int main(){
n=rd();
for(int i=1;i<=n;i++)
a[i].x=rd(),a[i].y=rd();
build(1,1,n-1);
for(int i=1;i<=n-1;i++)
printf("%d ",query(1,1,n-1,i+1,n-1,a[i],a[i+1]));
return 0;
}