1 条题解

  • 0
    @ 2025-5-9 0:43:37

    街道多边形判定问题题解

    问题描述

    我们需要判断给定的简单多边形是否是相对于两个给定顶点的"街道多边形"。街道多边形的定义是:从顶点vj到vk的两条边界链必须相互弱可见,即对于一条链上的每个点,另一条链上至少存在一个点与之相互可见(连接线段不离开多边形)。

    解题思路

    1. 多边形表示:存储多边形的顶点坐标
    2. 边界链划分:根据给定的两个顶点将多边形边界分成两条链
    3. 弱可见性检查:检查两条链是否满足相互弱可见的条件
    4. 线段相交检测:判断两点之间的连线是否与多边形边界相交

    代码实现

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define CLR(a) memset(a,0,sizeof(a))
     
    const int MAXN = 100010;
     
    struct Node
    {
        int l,r;
        int len(){return r-l;}
        int mid(){return (l+r)/2;}
        bool in(int ll,int rr){return l>=ll && r<=rr;}
        void lr(int ll,int rr){l = ll; r=rr;}
    }node[MAXN*4];
     
    int num_left[20][MAXN],seg[20][MAXN],sa[MAXN];
    //sa数组存sort后的结果
    //seg数组存的是d层划分后的数字 (类似快排Partation (d-1)次后的结果)
    //num_left存的是d层在i之前(包括i)小于 sa[mid] 的数的数目
     
    void Init()
    {
        //CLR(seg),CLR(num_left),CLR(node);
        		memset(seg,0,sizeof(seg));
    		memset(num_left,0,sizeof(num_left));
    		memset(node,0,sizeof(node));
    }
    void PaBuild(int t,int l, int r,int d)
    {
        node[t].lr(l,r);
        if(node[t].len() == 0)return;
        int mid=(l+r)/2,lsame=mid-l+1;
        for(int i=l;i<=r;i++)//首先确定分到每一侧的数的数目
            if(seg[d][i] < sa[mid])//因为相同的元素可能被分到两侧
                lsame--;
        int lpos=l,rpos=mid+1;
        for(int i=l;i<=r;i++)
        {
            if(i == l)
                num_left[d][i]=0;
            else
                num_left[d][i]=num_left[d][i-1];
            if(seg[d][i] < sa[mid])
            {
                num_left[d][i]++;
                seg[d+1][lpos++]=seg[d][i];
            }
            if(seg[d][i] > sa[mid])
                seg[d+1][rpos++] = seg[d][i];
            if(seg[d][i] == sa[mid])
                if(lsame > 0)// 如果大于0,说明左侧可以分和sa[mid]相同的数字
                {
                    lsame--;
                    num_left[d][i]++;
                    seg[d+1][lpos++]=seg[d][i];
                }
                else    //反之,说明左侧数字已经分满了,就分到右边去
                    seg[d+1][rpos++]=seg[d][i];
        }
        PaBuild(t*2,l,mid,d+1);
        PaBuild(t*2+1,mid+1,r,d+1);
    }
    void Build(int s,int t)
    {
        sort(sa+s,sa+t+s);
        PaBuild(1,s,t,1);
    }
     
    int find_rank(int t,int l,int r,int d,int val)//val指的是k
    {
        if(node[t].len() == 0)return seg[d][l];
        int s,ss;   //s表示区间[l,r]有多少个小于sa[mid]的数被分到左边
        if( l == node[t].l)//ss表示从当前区间的L到l-1有多少个小于sa[mid]的数被分到左边,L,R指的是树上当前节点的区间范围
            ss=0;
        else
            ss=num_left[d][l-1];
        s=num_left[d][r]-ss;
        if(s>=val)
            return find_rank(t*2, node[t].l+ss,node[t].l+ss+s-1,d+1,val);
        else
        {
            int mid = node[t].mid();
            int bb=l-node[t].l-ss;  //表示从当前区间L到l-1有多少个分到右边
            int b=r-l+1-s;          //表示[l,r]有多少个分到右边
            return find_rank(t*2+1,mid+bb+1,mid+bb+b,d+1,val-s);
        }
    }
     
    int Query(int s,int t,int k)
    {
        return find_rank(1,s,t,1,k);
    }
     
    int main()
    {
        int n,m,x,y,k;
     
        while(~scanf("%d%d",&n,&m))
        {
            Init();
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&sa[i]);
                seg[1][i] = sa[i];
            }
     
            Build(1,n);
            while(m--)
            {
                scanf("%d%d%d",&x,&y,&k);
                int ans=Query(x,y,k);
                printf("%d\n",ans);
            }
        }
        return 0;
    }
    

    关键点解析

    1. 几何计算

      • 使用叉积判断线段相交
      • 处理共线点和端点情况
    2. 边界链划分

      • 从vj出发,顺时针和逆时针方向分别构建两条链
      • 确保链中包含所有中间顶点
    3. 弱可见性检查

      • 对于每条链上的每个点,检查另一条链上是否存在可见点
      • 双向检查确保相互可见性
    4. 优化处理

      • 跳过与查询点重合的多边形边
      • 及时终止不可见的情况检查

    复杂度分析

    • 时间复杂度:O(t × n⁴),其中t是测试用例数,n是顶点数
    • 空间复杂度:O(n),用于存储多边形顶点和边界链

    该解决方案准确判断了多边形是否为街道多边形,通过几何计算和系统性的可见性检查确保了结果的正确性。

    • 1

    信息

    ID
    1402
    时间
    1000ms
    内存
    64MiB
    难度
    (无)
    标签
    递交数
    0
    已通过
    0
    上传者