uoj#57./bzoj3051 【WC2013】平面图 //平面图转对偶图

博客介绍了如何解决一道平面图题目,通过将平面图转化为对偶图并构建最小生成树来寻找连接两点的最优路径。在处理线段和区域时,采用了极角排序和线段树等数据结构,复杂度为O(nlog^2n)。建议在解决此类问题时确保有足够的耐心和时间进行调试。
摘要由CSDN通过智能技术生成

uoj#57. 【WC2013】平面图


题意

给出由M(<=1e5)条带权线段连接的N(<=1e5)个点,点横纵坐标为整数且在[0,1e7]间。线段只在顶点处相交。
Q(<=1e5)组询问,每组询问给定两个不在前述任意线段上的点A,B,满足其横纵坐标为0.5的奇数倍。
要求画一条不经过无界区域或顶点的曲线连接A,B,并使得其横穿的线段权值最大的最小。
判断是否有解,输出最优解下经过线段的最大权值。


题解

这道题的题解还是暑假学网络流的时候看到的…。
是一个听起来很高端实际上很暴力的东西。
几周前的模拟考了类似的东西,是平面图转对偶图套个动态点分治我当场竟然写出来了 结果又T又RE又MLE
扯远了…。进入正题。
一个显然的思路:
在每条线段两侧的有界区域间连边,值为线段的权值。
于是我们只需要对这堆区域建一个最小生成树,每次询问时树上倍增跳一跳就好了。
不过有一些令人恼火的实现问题,比如线段两侧分别对应哪个区域,或怎么确定A,B在哪个区域里。
第一个问题可以计算几何常识解决。
从一条边开始,每次走它终点的出边里与它顺(逆)时针夹角最小的,这么走一圈就是一个区域。
所以我们对每个点的出边极角排序,就可以处理出每条边应走的下一条边。
鉴于每条线段只会被正着走一次反着走一次,所以直接搜一下,能够知道每条边左(右)对应的区域。
因为带个排序,所以这一步是O(nlogn)的。
考虑第二个问题。
设当前询问点坐标为(Ax,Ay),画一条x=Ax的直线穿过一些线段,找到其中交点纵坐标第一个大于Ay的。这条线段下侧的区域即为所求。
这个事情可以拿个数据结构来维护。我们把每一条线段挂在 它在x方向上覆盖的区间 在线段树中所对应的log个节点上。
因为线段除顶点不交,它们在y方向上的相对位置必然不变。我们把每个节点挂着的线段排个序,查询就是按照Ax在线段树上单点查询,然后在log个节点包含的线段里二分出与x=Ax交点纵坐标大于Ay且最小的。
顺便,可以通过查一个横坐标在中间纵坐标为-1的点确定无界区域。
这里是O(nlog^2n)的。
最后是O(nlogn)的最小生成树,本题宣告结束。


注意事项

好想但是不大好写。
如果你对自己的码力没有绝对的信心,请在做这道题时确保自己拥有充足的时间和耐心。
可以写一段调一段,以确保不会最后一堆代码放到一起调不出来。
(本来还想写写过程中犯的错误,发现都是一些弱智错误 所以还是算了)


代码

在没删调试数据之前,这份代码长7k…。

#include<bits/stdc++.h>
#define N 100005
#define L 18
using namespace std;
typedef long long LL;
int n,m,q,to[N<<1],hd[N<<1],len[N<<1],lk[N],cnt=1,ln[N][L],
st0[N],st1[N],pre[N<<1],ref[N<<1],ff[N],f[N][L],dep[N],
non,t0,t1,tot,cct;//ref是记有向边左侧区域编号的
vector<int>vec[N<<2];//我因为这个开小了多调了1h QAQ
struct node
{
  int x,y;}a[N];
struct seg
{
  int u,v,w;}b[N];
int getr(int x)
{
  return ff[x]==x?x:ff[x]=getr(ff[x]);}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值