zoukankan      html  css  js  c++  java
  • BZOJ4049][CERC2014]Mountainous landscape-[线段树+凸包+二分]

    Description

    现在在平面上给你一条折线P1P2P3...Pn

    x坐标是严格单调递增的。对于每一段折线PiPi+1,请你找一个最小的j,使得j>i且走在PiPi+1的人能看到折线PjPj+1上的任意一点。

    注意,人的高度无限趋近0但不可忽略。也就是说,请找一条编号最小的折线PiPi+1使得j>i且线段PiPi+1(含端点)与略高于PiPi+1的射线相交。

    $2leq nleq 10^{5}$

    $0leq x_{i},y_{i}leq 10^{9}$

    Solution

    定义折线i为PiPi+1

    首先,对于某条折线i,我们无法通过某些公式或比较直接推定它对应的折线j。

    遇到这种情况,我们一般先考虑二分。及对于某条折线i,区间[l,r]之间是否有解。

    显然这里的判断可以使用凸包。只要该折线与点区间[l,r]之间的凸包上某条边有交点(当然要注意特判,假如折线与凸包交在顶点要算顶点后面的边),则我们可以判定i在[l,r]之间有解。所以用线段树维护凸包,查询时先查询线段树左区间,左区间无解再找右边。在凸包上的判断也可以用二分。(这个的具体判断方法见代码,画个图就好,证明可以利用叉积的几何意义)

    我的代码中,区间[l,r]表示的是点集[l,r+1],这样就可以确保最终答案是找到一条折线而不是一个点。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    int n;
    struct P{int x,y;
        friend P operator-(P a,P b){return P{a.x-b.x,a.y-b.y};}
        friend ll operator*(P a,P b){return 1ll*a.x*b.y-1ll*a.y*b.x;}
    }p[100010],st[2000010];int tp=0;
    int _l[400010],_r[400010];
    void seg_build(int k,int l,int r)
    {
        _l[k]=tp+1;
        for(int i=l;i<=r+1;i++)
        {
            while (tp-_l[k]>0&&(st[tp]-st[tp-1])*(p[i]-st[tp])>=0)
             tp--;
            st[++tp]=p[i];            
        }
        _r[k]=tp;    
        if (l==r) return;
        int mid=(l+r)/2;
        seg_build(k<<1,l,mid);
        seg_build(k<<1|1,mid+1,r);
    }
    bool check(int k,int id)
    {
        P a=p[id],v=p[id+1]-a;
        int l=_l[k],r=_r[k]-1,mid;
        while (l<r)
        {
            mid=(l+r)/2;
            if ((st[mid]-a)*v<(st[mid+1]-a)*v) r=mid;
            else l=mid+1;
        }
        return (st[l]-a)*v<0||(st[l+1]-a)*v<0;
    }
    int query(int k,int l,int r,int ask)
    {
        if (ask<=l)
        {
            if (!check(k,ask-1)) return 0;
            if (l==r) return l;
        }
        int mid=(l+r)/2;
        if (ask<=mid)
        {
            int re=query(k<<1,l,mid,ask);
            if (re) return re;
        }
        return query(k<<1|1,mid+1,r,ask);
    }
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            scanf("%d%d",&p[i].x,&p[i].y);
        seg_build(1,1,n-1);
        for (int i=1;i<n-1;i++) 
            printf("%d ",query(1,1,n-1,i+1));
        printf("0");
    }
  • 相关阅读:
    WCF异常管理—不要在using语句中调用WCF服务(z)
    正则表达式
    nginx汇总(z)
    WCF编解码实现
    zTree -- jQuery 树插件
    C# 程序异常管理方案
    WPF剪切板问题-OpenClipboard HRESULT:0x800401D0 (CLIPBRD_E_CANT_OPEN))
    乘法逆元
    二叉树后序遍历(非递归)
    P1892 [BOI2003]团伙
  • 原文地址:https://www.cnblogs.com/coco-night/p/9556164.html
Copyright © 2011-2022 走看看