题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2584

题意:给出平面n个线段,任意两个线段严格不相交,且每个线段不平行于坐标轴。移走所有线段。每次移走一个线段,移n次,移走时只能竖直向下、向上或水平向左向右移走。每次移动时不能与当前还有的其他线段相交(顶点与顶点相交允许)。要求解决两个问题:

(1)题目给出了一种移走的序列,但是这个序列是不合法的。找出这个序列中最早的不合法的移动是第几个;

(2)要求你给出一种合法的移动序列。

思路:首先,我们来解决第二个问题。我们发现,只向一个方向移动是可以找到一个解的,这其实是一个拓扑图,我们的目标就是确定这个拓扑关系。比如竖直向上移动。我们可以用扫描线+set维护的方法来解决。

对于第一个问题,我们可以把移走的过程倒着进行,变为添加的过程。假如我们找到了竖直向上移走的拓扑关系。那么对于一个移走时是向上的线段,添加时是从上向下添加的,只要当前已经添加的所有里面的线段在拓扑图上都不是当前要添加的线段的前驱,当前添加就是合法的。其他的添加类似。

因此,我们可以为拓扑图重新标号,然后用线段树维护区间最大最小值即可。

const int INF=1000000007;
const int N=100005; int a[N][4],b[N][2];
int n; int e[2][N][2],Max[2]; vector<int> g[2][N];
int mp[2][N]; int c[N*2],cNum; int curX; struct node
{
int id;
double k,b;
int x,flag; int operator<(const node &p) const
{
double xMin=k*curX+b;
double pxMin=p.k*curX+p.b;
return xMin>pxMin;
} int operator==(const node &p) const
{
return id==p.id;
} }; node f[N*2];
int fNum; int cmp(node a,node b)
{
if(a.x!=b.x) return a.x<b.x;
return a.flag>b.flag;
} set<node> S; int ind[N]; void cal(int t)
{
cNum=0;
for(int i=1;i<=n;i++)
{
c[++cNum]=a[i][0]+1;
c[++cNum]=a[i][2];
}
sort(c+1,c+cNum+1);
cNum=unique(c+1,c+cNum+1)-(c+1); Max[t]=cNum; fNum=0;
for(int i=1;i<=n;i++)
{
fNum++;
f[fNum].id=i;
f[fNum].k=1.0*(a[i][3]-a[i][1])/(a[i][2]-a[i][0]);
f[fNum].b=a[i][1]-f[fNum].k*a[i][0];
f[fNum].x=lower_bound(c+1,c+cNum+1,a[i][0]+1)-c;
f[fNum].flag=1; e[t][i][0]=f[fNum].x; fNum++;
f[fNum].id=i;
f[fNum].k=1.0*(a[i][3]-a[i][1])/(a[i][2]-a[i][0]);
f[fNum].b=a[i][1]-f[fNum].k*a[i][0];
f[fNum].x=lower_bound(c+1,c+cNum+1,a[i][2])-c;
f[fNum].flag=-1; e[t][i][1]=f[fNum].x;
} sort(f+1,f+fNum+1,cmp); S.clear();
set<node>::iterator it;
int cur=1;
clr(ind,0);
for(int i=1;i<=cNum;i++)
{
curX=c[i];
while(cur<=fNum&&f[cur].x==i)
{
if(1==f[cur].flag)
{
S.insert(f[cur]);
it=S.find(f[cur]);
if(it!=S.begin())
{
it--;
g[t][it->id].pb(f[cur].id);
ind[f[cur].id]++;
}
it=S.find(f[cur]);
it++;
if(it!=S.end())
{
g[t][f[cur].id].pb(it->id);
ind[it->id]++;
}
}
else S.erase(f[cur]); cur++;
}
} queue<int> Q;
int curId=0;
for(int i=1;i<=n;i++)
{
if(!ind[i]) Q.push(i),mp[t][i]=++curId;
}
while(!Q.empty())
{
int u=Q.front();
Q.pop(); for(int i=0;i<SZ(g[t][u]);i++)
{
int v=g[t][u][i];
if(0==--ind[v]) Q.push(v),mp[t][v]=++curId;
}
}
} struct SegNode
{
int L,R,Min,Max;
int detMin,detMax; void add(int x)
{
if(x<Min) Min=x;
if(x>Max) Max=x;
if(x<detMin) detMin=x;
if(x>detMax) detMax=x;
}
}; struct SegTree
{
SegNode A[N*8]; void build(int t,int L,int R)
{
A[t].L=L;
A[t].R=R;
A[t].Max=-INF;
A[t].Min=INF;
A[t].detMax=-INF;
A[t].detMin=INF;
if(L==R) return;
int M=(L+R)>>1;
build(t<<1,L,M);
build(t<<1|1,M+1,R);
} void pushDown(int t)
{
if(A[t].L==A[t].R) return;
if(A[t].detMax!=-INF)
{
A[t<<1].add(A[t].detMax);
A[t<<1|1].add(A[t].detMax);
A[t].detMax=-INF;
}
if(A[t].detMin!=INF)
{
A[t<<1].add(A[t].detMin);
A[t<<1|1].add(A[t].detMin);
A[t].detMin=INF;
}
} void pushUp(int t)
{
A[t].Min=min(A[t<<1].Min,A[t<<1|1].Min);
A[t].Max=max(A[t<<1].Max,A[t<<1|1].Max);
} void add(int t,int L,int R,int x)
{
if(A[t].L==L&&A[t].R==R)
{
A[t].add(x);
return;
}
pushDown(t);
int M=(A[t].L+A[t].R)>>1;
if(R<=M) add(t<<1,L,R,x);
else if(L>M) add(t<<1|1,L,R,x);
else
{
add(t<<1,L,M,x);
add(t<<1|1,M+1,R,x);
}
pushUp(t);
} pair<int,int> get(int t,int L,int R)
{
if(A[t].L==L&&A[t].R==R) return MP(A[t].Min,A[t].Max);
pushDown(t);
int M=(A[t].L+A[t].R)>>1;
if(R<=M) return get(t<<1,L,R);
if(L>M) return get(t<<1|1,L,R);
pair<int,int> aa=get(t<<1,L,M);
pair<int,int> bb=get(t<<1|1,M+1,R);
if(bb.first<aa.first) aa.first=bb.first;
if(bb.second>aa.second) aa.second=bb.second;
return aa;
} }A[2]; int get()
{
A[0].build(1,1,Max[0]);
A[1].build(1,1,Max[1]);
int ans=-1;
for(int i=n;i>=1;i--)
{
int t=b[i][0];
int d=b[i][1];
if(0==d||2==d)
{
int L=e[1][t][0];
int R=e[1][t][1];
pair<int,int> p=A[1].get(1,L,R);
if(0==d&&p.second>mp[1][t]||2==d&&p.first<mp[1][t]) ans=i; }
else
{
int L=e[0][t][0];
int R=e[0][t][1];
pair<int,int> p=A[0].get(1,L,R);
if(3==d&&p.second>mp[0][t]||1==d&&p.first<mp[0][t]) ans=i; }
A[0].add(1,e[0][t][0],e[0][t][1],mp[0][t]);
A[1].add(1,e[1][t][0],e[1][t][1],mp[1][t]);
}
return ans;
} int h[N]; int main()
{ n=myInt();
for(int i=1;i<=n;i++)
{
a[i][0]=myInt();
a[i][1]=myInt();
a[i][2]=myInt();
a[i][3]=myInt();
}
for(int i=1;i<=n;i++)
{
b[i][0]=myInt();
b[i][1]=myInt();
} for(int i=1;i<=n;i++) if(a[i][0]>a[i][2])
{
swap(a[i][0],a[i][2]);
swap(a[i][1],a[i][3]);
}
cal(0);
for(int i=1;i<=n;i++)
{
swap(a[i][0],a[i][1]);
swap(a[i][2],a[i][3]);
}
for(int i=1;i<=n;i++) if(a[i][0]>a[i][2])
{
swap(a[i][0],a[i][2]);
swap(a[i][1],a[i][3]);
}
cal(1); int ans=get();
printf("%d\n",ans);
for(int i=1;i<=n;i++) h[mp[0][i]]=i;
for(int i=1;i<=n;i++) printf("%d 1\n",h[i]);
}

  

BZOJ 2584: [Wc2012]memory(扫描线+线段树)的更多相关文章

  1. HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  2. 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

    [BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...

  3. HDU 3265/POJ 3832 Posters(扫描线+线段树)(2009 Asia Ningbo Regional)

    Description Ted has a new house with a huge window. In this big summer, Ted decides to decorate the ...

  4. 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树

    题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...

  5. hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

    题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...

  6. [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)

    [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并) 题面 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1 ...

  7. [BZOJ 2653] middle(可持久化线段树+二分答案)

    [BZOJ 2653] middle(可持久化线段树+二分答案) 题面 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整. 给你一个长度为n的序 ...

  8. P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)

    题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...

  9. [BZOJ 1218] [HNOI2003] 激光炸弹 【n logn 做法 - 扫描线 + 线段树】

    题目链接:BZOJ - 1218 题目分析 可以覆盖一个边长为 R 的正方形,但是不能包括边界,所以等价于一个边长为 R - 1 的正方形. 坐标范围 <= 5000 ,直接 n^2 的二维前缀 ...

随机推荐

  1. C# 多线程 lock 实例

    class Program { static void Main(string[] args) { //在t1线程中调用LockMe,并将deadlock设为true(将出现死锁) int i = 1 ...

  2. 记linux下使用create_ap 创建热点失败及解决(涉及rfkill)

    先介绍一下 create_ap. 这是一个在linux中创建热点用的脚本, 托管在github中, https://github.com/oblique/create_ap/ 正文开始: 习惯了win ...

  3. 前端控制器DispatcherServlet 详解

    DispatcherServlet 是前端控制器设计模式的实现,提供 Spring Web MVC 的集中访问点,而且负责职责的分派,而且与 Spring IoC 容器无缝集成,从而可以获得 Spri ...

  4. mysql笔记03 查询性能优化

    查询性能优化 1. 为什么查询速度会慢? 1). 如果把查询看作是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.如果要优化查询,实际上要优化其子任务,要么消除其中一些子任务,要么减 ...

  5. datatables中columns.render的使用

    可直接在columns申明中对应列下方使用render改变该列样式 也可单独在columnsDefs中用targets指定目标. "render":function(data,ty ...

  6. ubuntu下搭建JAVA开发环境【转】

    转自:http://jingyan.baidu.com/article/86fae346b696633c49121a30.html JAVA开发环境是一种跨平台的程序设计语言,可以在windows.L ...

  7. Java 基本数据类型 sizeof 功能【转】

    转自:http://blog.csdn.net/sunboy_2050/article/details/7310008 版权声明:本文为博主原创文章,未经博主允许不得转载. Java基本数据类型int ...

  8. https笔记

    TCP提供了可靠的,面向连接的字节流服务. 1)应用数据分割成TCP认为适合发送的数据块,通过MSS(最大数据包长度)来控制. 2)重传机制 3)对首部和数据进行校验 4)TCP对收到的数据进行排序, ...

  9. 【转载】perl接受传递参数的方法

    #! /usr/bin/perl use Getopt::Std;use warnings;use strict; sub read_from_sh($) { my $file = shift; my ...

  10. IOS 音频开发文件大小计算

    音频基础知识 音频文件计算大小 音频转码 标签(空格分隔): 调查 IOS音频 https://developer.apple.com/library/ios/documentation/MusicA ...