Files
python/TangDou/AcWing/SegmentTree/HDU5820.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

122 lines
5.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}