You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
5.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
//快读
int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
const int N = 500010;
const int M = 50010;
int n; //点的数量
vector<int> a[M]; //记录每个点的坐标
//主席树的结构体
struct Node {
int cnt;
int l, r;
} tr[N << 5]; //主席树一般开32倍空间
int idx; //主席树的节点生成器
int root[M]; //版本与根节点的对应关系
int ver[M]; //记录y坐标是在哪个版本加入进去的,这是一个桶性能用静态数组模拟应该是最强的不要使用什么unordered_map之流
//标准的主席树insert
int insert(int p, int l, int r, int x) {
int q = ++idx;
tr[q] = tr[p];
tr[q].cnt++;
if (l == r) return q;
int mid = (l + r) >> 1;
if (x <= mid)
tr[q].l = insert(tr[p].l, l, mid, x);
else
tr[q].r = insert(tr[p].r, mid + 1, r, x);
return q;
}
//功能:主席树,在两个版本间 查询 某个区间中数字的增加个数
int query(int q, int p, int ql, int qr, int l, int r) {
if (ql <= l && qr >= r) return tr[p].cnt - tr[q].cnt; //完整区间包含,直接返回两个版本的数字个数差,表示两个版本间增加的数字数量
int ans = 0;
int mid = (l + r) >> 1;
if (ql <= mid) ans = query(tr[q].l, tr[p].l, ql, qr, l, mid); //递归查询左半区间
if (qr > mid) ans += query(tr[q].r, tr[p].r, ql, qr, mid + 1, r); //递归查询右半区间
return ans;
}
bool solve() {
//对于主席树的0号版本进行清空
root[0] = idx = tr[0].l = tr[0].r = tr[0].cnt = 0; //索引号生成器idx修改为0主席树中0号结点左儿子右儿子都不存在0号版本中数字个数为0, 0号版本的根节点是0
memset(ver, 0, sizeof(ver)); //清空ver数组
for (int i = 1; i <= 50000; i++) { //从小到大枚举每一个x坐标
for (int j = 0; j < a[i].size(); j++) { //从小到大枚举每一个y坐标
//如果是第一个y坐标那么l=0,这个0是哨兵的意思。如果不是第一个y坐标那么l=当前y坐标的前一个y坐标prev_y,
int l = j == 0 ? 0 : a[i][j - 1];
//如果是最后一个y坐标那么r=50001,否则r=当前y坐标的下一个y坐标next_y
int r = j == a[i].size() - 1 ? 50001 : a[i][j + 1];
// root[i]:当前版本的根
// root[x[a[i][j]]]:前一版本的根
// query的返回值是个整数表示两个版本间两个区间内增加的数字个数如果个数大于0就return false
if (query(root[i - 1], root[ver[a[i][j]]], l + 1, r - 1, 1, 50000) > 0) return false;
}
root[i] = root[i - 1];
for (int j = 0; j < a[i].size(); j++) { //从小到大枚举每一个y坐标
root[i] = insert(root[i], 1, 50000, a[i][j]);
ver[a[i][j]] = i; //记录a[i][j]这个y坐标是第i个版本中增加进去的
}
}
//如果上面所有的坐标点都讨论过全都没有返回false,那就只能返回true
return true;
}
/*
NO
YES
*/
int main() {
//文件输入输出
#ifndef ONLINE_JUDGE
freopen("HDU5820.in", "r", stdin);
#endif
while (n = read(), n) { //在使用快读时,要注意使用 ,n 而不是使用 && n!!!! read没有返回值
// a数组装的是坐标(x,y),只不过它是以x坐标为第一维第二维的y1,y2,y3,... 通过push_back加入到一维的x中
for (int i = 0; i < M; i++) a[i].clear(); //二维vector的清空操作不用循环还真不行
int x, y;
while (n--) {
x = read(), y = read(); // 每个点的坐标
a[x].push_back(y); // 记录每个点的坐标记录的方法很有意思是一个vector,一维是x坐标二维是y坐标
}
//枚举每个可能的x坐标对x坐标相同的点按y坐标小由到大进行排序这样就是一个二维有序的数组啦和结构体两个属性进行排序效果和速度是一样的
for (int i = 1; i <= 50000; i++) { //这样是以x从小到大排序的
sort(a[i].begin(), a[i].end()); //对y也要从小到大排序,这和整一个Node{x,y},然后自定义sort operator < 定义先按x后按y是一个意思有时间再写一个结构体版本的
a[i].erase(unique(a[i].begin(), a[i].end()), a[i].end()); //对于相同的(x,y)进行排序+去重
}
//开始每一轮计算的主函数solve
puts(solve() ? "YES" : "NO"); //存在题目要求的答案返回YES否则返回NO
}
return 0;
}