uoj#57. 【WC2013】平面图
题意
【线段树|uoj#57./bzoj3051 【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
#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是记有向边左侧区域编号的
vectorvec[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]);
}
inline void add(int u,int v,int w)
{to[++cnt]=v,hd[cnt]=lk[u],len[cnt]=w,lk[u]=cnt;
}
int i,tp0,tp1,ans;
void ins(int k,int l,int r,int ll,int rr)
{
if(l==ll&&r==rr)
{vec[k].push_back(i);
return;
}
int mid=l+r>>1;
if(rr<=mid)ins(k<<1,l,mid,ll,rr);
else if(ll>mid)ins(k<<1|1,mid+1,r,ll,rr);
else ins(k<<1,l,mid,ll,mid),ins(k<<1|1,mid+1,r,mid+1,rr);
}
double tmp;
inline double loc(int x,double xx)
{
return a[b[x].u].y+(a[b[x].v].y-a[b[x].u].y)/
(double)(a[b[x].v].x-a[b[x].u].x)*(xx-a[b[x].u].x);
}//线段x与x=xx的交点
void qry(int k,int l,int r,double x,double y)
{
int mid=l+r>>1;
if(l>1,tp=loc(vec[k][mid],x);
if(y>1;
cal(k<<1,l,mid),cal(k<<1|1,mid+1,r);
}
}
bool cmp(int x,int y)
{return (a[to[x]].x-a[i].x)*(LL)(a[to[y]].y-a[i].y)
-(a[to[y]].x-a[i].x)*(LL)(a[to[x]].y-a[i].y)<0;
}
inline int Q(double x,double y)
{
tmp=1e8,ans=-1;
qry(1,1,t1-1,x,y);
return ans<0?non:ref[ans<<1|1];
}
bool Cmp(seg x,seg y)
{return x.wln[x][j-1]?
ln[f[x][j-1]][j-1]:ln[x][j-1];
f[x][j]=f[f[x][j-1]][j-1];
if(!f[x][j])break;
}
for(int k,j=lk[x];
j;
j=hd[j])
if(!dep[k=to[j]])
f[k][0]=x,ln[k][0]=len[j],dfss(k);
}
double ax,ay;
int main()
{
scanf("%d%d",&n,&m);
for(i=1;
i<=n;
i++)
scanf("%d%d",&a[i].x,&a[i].y);
for(i=1;
i<=m;
i++)
{
scanf("%d%d%d",&b[i].u,&b[i].v,&b[i].w);
if(a[b[i].u].x>a[b[i].v].x)swap(b[i].u,b[i].v);
}
sort(b+1,b+m+1,Cmp);
for(i=1;
i<=m;
i++)
add(b[i].u,b[i].v,0),add(b[i].v,b[i].u,0);
for(i=1;
i<=n;
i++)
{
t0=t1=0;
for(int j=lk[i];
j;
j=hd[j])
if(a[to[j]].yelse st1[t1++]=j;
sort(st0,st0+t0,cmp);
sort(st1,st1+t1,cmp);
for(int j=0;
jst1[t1-1])?non:Q(ax,ay);
scanf("%lf%lf",&ax,&ay);
tp1=(ax<=st1[0]+1e-9||ax+1e-9>st1[t1-1])?non:Q(ax,ay);
if(tp0==non||tp1==non||(getr(tp0)^getr(tp1)))
{puts("-1");
continue;
}
if(dep[tp0]=0&&dep[tp0]>dep[tp1];
i--)
if(dep[f[tp0][i]]>=dep[tp1])
ans=ans=0;
i--)
if(f[tp0][i]^f[tp1][i])
ans=ans
推荐阅读
- 线段树|[类欧几里得算法 线段树] BZOJ 1938 [CROATIAN2010] ALADIN
- 牛客 C. 子段乘积(线段树)
- 牛客挑战赛39 C 牛牛的等差数列(线段树)(*)
- 牛客 数据结构(区间修改+求区间元素平方和)
- 校门外的树 线段树版
- 树链剖分|牛客练习赛51 F-ABCBA(树链剖分,线段树,状态转移)
- 数论|bzoj 1938 - 类欧几里得+线段树
- 线段树|FZU - 2277(树链剖分或dfs序+线段树)
- 并查集|[CF938G] Shortest Path Queries
- 牛客网 练习赛25 B最长区间