题目描述
题目大意:在二维平面中有n个线段,线段互不相交,每一次一个操作将一个线段向四个方向之一移走(也就是平移到无穷远处),如果移动的过程中被其他线段所阻挡则移动是不合法的。求:最早的一次不合法移动;构造一种合法的移动序列将所有线段移走
题解
首先考虑第二问,实际上所有的线段都按照任意一个固定的方向移动就是可以直接都移走的,所以我们需要将线段编号。因为线段均不相交,所以对于任意两条线段的所有横坐标x,它们所对应的纵坐标y的相对位置是不变的,所以可以用splay来维护。将线段拆成左端点和右端点,按照x坐标排序做扫描线,扫到线段的左端点就在splay中插入一个点,插入的过程中需要计算之前splay中的线段在横坐标为x时的纵坐标y来判断插入的位置。遇到线段的右端点就在splay中删除。每一次插入之后找一下这个点的前驱后继并且由前驱向这个点、由这个点向后继连边,这样连出来一个DAG,求拓扑序即为合法的删除序列。
在第二问的基础上考虑第一问,横竖做两遍扫描线可以求出来两个方向上的拓扑序。将线段的正序删除转化为倒序插入,每一次看这条线段是否能移动需要查询一下在这个方向上、这个线段范围内已经插入的线段的拓扑序的最大值和最小值,如果有空的才能向那个方向移动。每一次插入将这个线段所控制的区间更新一下,可以用线段树来实现。注意由于端点相交是合法的,所以离散化的时候需要在两个点之间都插入一个点。
可是这道题我用wc2012的官方数据评测都没有问题,交到bzoj上wa了,询问管理员得到了模棱两可的回复。。懒得管了。。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 200005
struct Point
{
int x,y;
Point(int X=0,int Y=0)
{
x=X,y=Y;
}
};
struct Segment
{
Point a,b;
Segment(Point A=Point(0,0),Point B=Point(0,0))
{
a=A,b=B;
}
void read(){
scanf("%d%d%d%d",&a.x,&a.y,&b.x,&b.y);}
void check(){
if (a.x>b.x) swap(a.x,b.x),swap(a.y,b.y);}
void Swap(){swap(a.x,a.y),swap(b.x,b.y);}
}line[N];
struct OPT {
int x,id,ty;}opt[N<<1];
struct MOVE {
int id,dir;}move[N];
int n,inf,Min,Max,LSH,lsh[N<<1],ans1,ans2[N];
int root,sz,f[N],ch[N][2],id_in_splay[N],id_in_graph[N];double K[N],B[N];
int tot,point[N],nxt[N<<1],v[N<<1],in[N],ver[N],hor[N];
int minn[N*20],maxn[N*20],deltamx[N*20],deltamn[N*20];
queue <int> q;
//---------------------------------------splay------------------------------------------
void splay_clear(int x)
{
f[x]=ch[x][0]=ch[x][1]=id_in_graph[x]=0;
K[x]=B[x]=0;
}
int get(int x)
{
return ch[f[x]][1]==x;
}
void rotate(