HDU 5820 Lights (2016多校7L,主席树)

时间:2022-05-26 19:36:04

题意  给定n个平面上的点,坐标范围为[1, 50000]。如果对于任意两个点,都可以通过直走(中途经过其他点)走到。

   那么输出YES,否则输出NO。

 

首先排序,去重。

我们要找的点对是只能斜对角走到的点。

那么找到这个点正左边的离他最近的点和正上方最近的点。查询以这三个点为顶点的矩形的内部有没有其他点。

如果有的话就找到了这样的点对,输出NO。

用主席树实现,时间复杂度$O(nlogn)$。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second

typedef long long LL;
typedef pair <int, int> PII;

const int N = 1e6 + 10;
const int M = 5e4 + 10;

PII a[N];
map <PII, int> mp;
int n, flag;
int T;
int num;
int mx[M], my[M];
int tot;
int s[N << 3], ls[N << 3], rs[N << 3];
int root[M];

void update(int &x, int y, int l, int r, int pos){
	x = ++tot;
	s[x] = s[y] + 1;
	if (l == r) return;
	ls[x] = ls[y];
	rs[x] = rs[y];
	int mid = (l + r) >> 1;
	if (pos <= mid) update(ls[x], ls[y], l, mid, pos);
	else update(rs[x], rs[y], mid + 1, r, pos);
}

int query(int x, int y, int L, int R, int l, int r){
	if (l <= L && R <= r) return s[y] - s[x];
	int ret = 0;
	int mid = (L + R) >> 1;
	if (l <= mid) ret += query(ls[x], ls[y], L, mid, l, r);
	if (r >  mid) ret += query(rs[x], rs[y], mid + 1, R, l, r);
	return ret;
}

void work1(){
	tot = 0;
	memset(mx, 0, sizeof mx);
	memset(my, 0, sizeof my);
	memset(root, 0, sizeof root);
	int last = root[0];
	rep(i, 1, n){
		int x1 = a[i].fi, y1 = a[i].se;
		int x2 = a[my[y1]].fi, y2 = a[mx[x1]].se;
		update(root[x1], last, 1, 50000, y1);
		int cnt = query(root[x2], root[x1], 1, 50000, y2 + 1, y1);
		if (cnt != 1){
			flag = 0;
			return;
		}
		mx[a[i].fi] = i;
		my[a[i].se] = i;
		last = root[x1]; 
	}
}

int main(){

	while (~scanf("%d", &n), n){
		rep(i, 1, n){
			scanf("%d%d", &a[i].fi, &a[i].se);
		}

		a[n + 1].fi = a[n + 1].se = 0;

		sort(a + 1, a + n + 1);
		n = unique(a + 1, a + n + 1) - a - 1;

		flag = 1;
		work1(); 
		rep(i, 1, n) a[i].fi = 50001 - a[i].fi;
		sort(a + 1, a + n + 1);
		work1();
		puts(flag ? "YES" : "NO" );
	}

	return 0;
}