【BZOJ4049】[Cerc2014] Mountainous landscape(线段树+凸包+二分)

题意:给定一个折线图,按x轴递增的顺序给出。对于每个条line,求出在它之后,且下标最小的line。输出这个下标。 其中 n100000 n ≤ 100000
首先我们需要用线段树,每个节点维护这个节点所表示的区间的点的凸包。
查询的时候,我们可以判断当前的区间的凸包是否与原直线有交点,如果有的话就递归左子树,如果左子树的凸包存在与原直线的交点,那么就可以直接返回,否则再递归右子树判断。
快速判断一个凸包与一条直线是否有交点,我们可以考虑二分,利用向量叉积的几何意义即可。具体就看看代码吧。
如果有误在评论区吼一声哦!
代码:

#include
#include
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{
    vectorc;
    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(lint 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;
}

你可能感兴趣的:(线段树,凸包,二分)