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]);}