1 条题解
-
0
街道多边形判定问题题解
问题描述
我们需要判断给定的简单多边形是否是相对于两个给定顶点的"街道多边形"。街道多边形的定义是:从顶点vj到vk的两条边界链必须相互弱可见,即对于一条链上的每个点,另一条链上至少存在一个点与之相互可见(连接线段不离开多边形)。
解题思路
- 多边形表示:存储多边形的顶点坐标
- 边界链划分:根据给定的两个顶点将多边形边界分成两条链
- 弱可见性检查:检查两条链是否满足相互弱可见的条件
- 线段相交检测:判断两点之间的连线是否与多边形边界相交
代码实现
#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; }
关键点解析
-
几何计算:
- 使用叉积判断线段相交
- 处理共线点和端点情况
-
边界链划分:
- 从vj出发,顺时针和逆时针方向分别构建两条链
- 确保链中包含所有中间顶点
-
弱可见性检查:
- 对于每条链上的每个点,检查另一条链上是否存在可见点
- 双向检查确保相互可见性
-
优化处理:
- 跳过与查询点重合的多边形边
- 及时终止不可见的情况检查
复杂度分析
- 时间复杂度:O(t × n⁴),其中t是测试用例数,n是顶点数
- 空间复杂度:O(n),用于存储多边形顶点和边界链
该解决方案准确判断了多边形是否为街道多边形,通过几何计算和系统性的可见性检查确保了结果的正确性。
- 1
信息
- ID
- 1402
- 时间
- 1000ms
- 内存
- 64MiB
- 难度
- (无)
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者