【BZOJ】2078: [POI2004]WYS

题意:

给n个互不相交的多边形(边均平行于坐标轴),问最大深度。深度的定义是,若多边形A被多边形B包含,则\(dep[A]=max(dep[B])+1\)。坐标系的深度为0。(n<=40000,顶点个数<=200000)

题解:

扫描线+动态维护区间。
考虑从左往右枚举交x轴平行于y轴的扫描线,维护每一个多边形在这条扫描线上能包含的最大\(y\)区间。
在任意时刻,这段区间都是连续的,因此我们只需维护两个端点。

将点离散,扫描线扫描的是所有点的x轴坐标。将所有点按照x为第一关键字、y为第二关键字,升序排序。然后从小到大枚举每个点。

令当前枚举到的点为\(p\)点,所在多边形为\(A\)
然后我们在这些要么包含要么不交的区间的端点中找一个点\(q\),满足\(y_q>y_p\)\(y_q\)最小,\(q\)所在多边形为\(B\)
由于我们是从左往右扫,而且多边形互不相交,因此问题转化为求当前端点被多少个区间包含的问题。

考虑这两种情况:
1、被\(q\)点所在区间包含,则\(dep[A]=dep[B]+1\)
2、没有被包含,则\(dep[A]=dep[B]\)

那么我们如何判断包含呢?我们在维护的两个端点打上这样的标记:
一个多边形的边\((u, v)\),方向为逆时针方向,假设方向为\((u->v)\),则我们标记边的终点\(v\)
则可以发现如果这个\(q\)是有标记的,则\(A\)没有被\(B\)包含。否则被\(B\)包含。
证明就是分类讨论几种情况即可。

现在来考虑如何维护每个多边形包含的区间。
由于一个多边形内每个点的\(x\)轴出现次数一定是偶数次。而由于排序后的性质,每条有向边的起点就加入到区间,终点则去掉。

到此本题解决。

#include <bits/stdc++.h>
using namespace std;
const int N=200005;
int n, d[40005], ans, tot;
struct ip {
	int x, y, id;
	bool flag;
	bool operator < (const ip &a) const {
		return x==a.x?y<a.y:x<a.x;
	}
}p[N];
map<int, ip *>mp;
void work() {
	sort(p+1, p+1+tot);
	map<int, ip *>::iterator it;
	for(int i=1; i<=tot; ++i) {
		int y=p[i].y;
		it=mp.find(y);
		if(it!=mp.end()) {
			mp.erase(it);
		}
		else {
			mp[y]=&p[i];
			int id=p[i].id;
			if(!d[id]) {
				it=mp.upper_bound(y);
				if(it==mp.end()) {
					d[id]=1;
				}
				else {
					if(it->second->flag) d[id]=d[it->second->id];
					else d[id]=d[it->second->id]+1;
				}
			}
		}
	}
	for(int i=1; i<=n; ++i) {
		ans=max(ans, d[i]);
	}
}
int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; ++i) {
		int k, x[2], t;
		scanf("%d%d", &k, &x[0]);
		t=x[0];
		for(int j=1; j<=k; ++j) {
			if(j!=k) scanf("%d", &x[j&1]);
			else x[0]=t;
			p[++tot]=(ip){x[0], x[1], i, (bool)(j&1)};
		}
	}
	work();
	printf("%d\n", ans);
	return 0;
}
posted @ 2015-11-22 13:34  iwtwiioi  阅读(341)  评论(0编辑  收藏  举报