- TopAI
ChatGPT-5
- @ 2025-8-27 21:28:28
题目描述
对一家有 n 个房间(编号为 1∼n,开始都为空房)的宾馆维护以下操作:
查询房间:你需要在 1,2,…,n 房间中找到长度为 x 的连续空房。若找得到,在这 x 个空房间中住上人。
退房:房间号 x∼x+y−1 退房,即让房间为空。
输入格式
第一行输入 n,m,n 代表有 n 个房间 (1≤n≤50,000),编号为 1∼n,开始都为空房,m 表示以下有 m 行操作 (1≤m≤50,000),以下每行先输入一个数 i,表示一种操作:
若 i 为 1,表示查询房间,再输入一个数 x。
若 i 为 2,表示退房,再输入两个数 x,y。 输出格式
对每个输入 1,输出连续 x 个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为 x 的连续空房,输出 0。
// 房间安排
// P2894
#include <cstdio>
#define lc u << 1
#define rc u << 1 | 1
#define max(x, y) x > y ? x : y
const int N = 50000 + 2047;
int n, m;
struct Tree {
int l, r;
int len, ans;
int lmax, rmax;
int tag;
}tr[N << 2];
void pushup(int u) {
tr[u].lmax = tr[lc].lmax;
tr[u].rmax = tr[rc].rmax;
if (tr[lc].lmax == tr[lc].len) tr[u].lmax += tr[rc].lmax;
if (tr[rc].rmax == tr[rc].len) tr[u].rmax += tr[lc].rmax;
tr[u].ans = max(tr[rc].lmax + tr[lc].rmax, max(tr[lc].ans, tr[rc].ans));
}
void pushdown(int u) {
if (tr[u].tag == -1) return;
tr[lc].tag = tr[rc].tag = tr[u].tag;
tr[lc].ans = tr[lc].lmax = tr[lc].rmax = tr[u].tag ? 0 : tr[lc].len;
tr[rc].ans = tr[rc].lmax = tr[rc].rmax = tr[u].tag ? 0 : tr[rc].len;
tr[u].tag = -1;
}
void build(int u, int l, int r) {
int x = r - l + 1;
tr[u] = {l, r, x, x, x, -1};
if (l == r) return;
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
}
void change(int u, int l, int r, int x) {
if (l <= tr[u].l && tr[u].r <= r) {
if (!x) {
int y = tr[u].len;
tr[u] = {tr[u].l, tr[u].r, y, y, y, y, x};
} else {
tr[u] = {tr[u].l, tr[u].r, tr[u].len, 0, 0, 0, x};
}
return;
}
pushdown(u);
int mid = l + r >> 1;
if (l <= mid) change(lc, l, r, x);
if (r > mid) change(rc, l, r, x);
pushup(u);
}
int query(int u, int x) {
if (tr[u].l == tr[u].r) return tr[u].l;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (tr[lc].ans >= x) {
return query(lc, x);
} else if (tr[rc].lmax + tr[lc].rmax >= x) {
return mid - tr[lc].rmax + 1;
} else {
return query(rc, x);
}
}
int main()
{
scanf("%d %d", &n, &m);
build(1, 1, n);
for (int k, x, y; m--;) {
scanf("%d %d", &k, &x);
switch (k) {
case 1 :
if (tr[1].ans < x) puts("0");
else {
int l = query(1, x);
printf("%d\n", l);
change(1, l, l + x - 1, 1);
}
break;
case 2 :
scanf("%d", &y);
change(1, x, x + y - 1, 0);
break;
}
}
return 0;
}
按原思路修改代码
4 条评论
-
-
WA,8分,继续改
// 房间安排 // P2894 #include <cstdio> #define lc u << 1 #define rc u << 1 | 1 #define max(x, y) x > y ? x : y const int N = 50000 + 2047; int n, m; struct Tree { int l, r; int len, ans; int lmax, rmax; int tag; }tr[N << 2]; void pushup(int u) { tr[u].lmax = tr[lc].lmax; tr[u].rmax = tr[rc].rmax; if (tr[lc].lmax == tr[lc].len) tr[u].lmax += tr[rc].lmax; if (tr[rc].rmax == tr[rc].len) tr[u].rmax += tr[lc].rmax; tr[u].ans = max(max(tr[lc].ans, tr[rc].ans), tr[lc].rmax + tr[rc].lmax); } void pushdown(int u) { if (tr[u].tag == -1) return; int t = tr[u].tag; tr[lc].tag = tr[rc].tag = t; tr[lc].ans = tr[lc].lmax = tr[lc].rmax = t ? 0 : tr[lc].len; tr[rc].ans = tr[rc].lmax = tr[rc].rmax = t ? 0 : tr[rc].len; tr[u].tag = -1; } void build(int u, int l, int r) { int s = r - l + 1; tr[u] = {l, r, s, s, s, s, -1}; if (l == r) return; int mid = l + r >> 1; build(lc, l, mid); build(rc, mid + 1, r); } void change(int u, int l, int r, int x) { if (l <= tr[u].l && tr[u].r <= r) { tr[u].tag = x; tr[u].lmax = tr[u].rmax = tr[u].ans = x ? 0 : tr[u].len; return; } pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) change(lc, l, r, x); if (r > mid) change(rc, l, r, x); pushup(u); } int query(int u, int x) { if (tr[u].l == tr[u].r) return tr[u].l; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (tr[lc].ans >= x) return query(lc, x); if (tr[lc].rmax + tr[rc].lmax >= x) return mid - tr[lc].rmax + 1; return query(rc, x); } int main() { scanf("%d %d", &n, &m); build(1, 1, n); while (m--) { int k, x, y; scanf("%d", &k); switch (k) { case 1 : scanf("%d", &x); if (tr[1].ans < x) puts("0"); else { int l = query(1, x); printf("%d\n", l); change(1, l, l + x - 1, 1); } break; case 2 : scanf("%d %d", &x, &y); change(1, x, x + y - 1, 0); break; } } return 0; } -
我看了你的代码,整体思路是标准的线段树维护区间最长连续空房间的方案,pushup/pushdown 逻辑是对的,不过你现在的
build、change、struct初始化存在几个问题,导致它和原题思路有些出入。我帮你按原思路改了一版,保留了核心写法,同时修正了几个细节:
改动要点
-
结构体成员顺序和初始化
你的Tree结构体里len、ans、lmax、rmax的赋值有些乱,构造时建议显式写出全部成员,避免{}顺序错乱。 -
build 函数
原思路里初始状态是所有房间为空,所以:lmax = rmax = ans = lentag = 0表示空房(或用 -1 表示无懒标记)
-
pushup
保持原逻辑:lmax要考虑左子区间全空时累加右子区间的lmax,rmax同理。 -
pushdown
注意这里 lazy 标记如果是 0 表示“全空”,1 表示“全占”,-1 表示“无标记”。你原本的初始化tag = -1是对的,但在build里要保证一致。 -
change
原思路:直接整段覆盖成全空或全占,ans、lmax、rmax对应赋 0 或len。不要用构造体整体替换(容易丢掉tag语义),建议逐个成员赋值。 -
query
保留原逻辑,先递归左子区间,如果不行再考虑跨区间,再去右子区间。
修改后代码(按原题思路修正版)
#include <cstdio> #define lc (u << 1) #define rc (u << 1 | 1) #define max(x, y) ((x) > (y) ? (x) : (y)) const int N = 50000 + 2047; int n, m; struct Tree { int l, r; // 区间左右端点 int len; // 区间长度 int ans; // 区间内最大连续空房数 int lmax, rmax; // 左端最大连续空房、右端最大连续空房 int tag; // lazy标记: -1无标记, 0全空, 1全占 } tr[N << 2]; void pushup(int u) { tr[u].lmax = tr[lc].lmax; tr[u].rmax = tr[rc].rmax; if (tr[lc].lmax == tr[lc].len) tr[u].lmax += tr[rc].lmax; if (tr[rc].rmax == tr[rc].len) tr[u].rmax += tr[lc].rmax; tr[u].ans = max(max(tr[lc].ans, tr[rc].ans), tr[lc].rmax + tr[rc].lmax); } void pushdown(int u) { if (tr[u].tag != -1) { int t = tr[u].tag; tr[lc].tag = tr[rc].tag = t; tr[lc].lmax = tr[lc].rmax = tr[lc].ans = t ? 0 : tr[lc].len; tr[rc].lmax = tr[rc].rmax = tr[rc].ans = t ? 0 : tr[rc].len; tr[u].tag = -1; } } void build(int u, int l, int r) { tr[u].l = l; tr[u].r = r; tr[u].len = r - l + 1; tr[u].lmax = tr[u].rmax = tr[u].ans = tr[u].len; tr[u].tag = 0; // 初始全空 if (l == r) return; int mid = (l + r) >> 1; build(lc, l, mid); build(rc, mid + 1, r); } void change(int u, int l, int r, int val) { if (l <= tr[u].l && tr[u].r <= r) { tr[u].tag = val; tr[u].lmax = tr[u].rmax = tr[u].ans = val ? 0 : tr[u].len; return; } pushdown(u); int mid = (tr[u].l + tr[u].r) >> 1; if (l <= mid) change(lc, l, r, val); if (r > mid) change(rc, l, r, val); pushup(u); } int query(int u, int x) { if (tr[u].l == tr[u].r) return tr[u].l; pushdown(u); if (tr[lc].ans >= x) return query(lc, x); if (tr[lc].rmax + tr[rc].lmax >= x) return tr[lc].r - tr[lc].rmax + 1; return query(rc, x); } int main() { scanf("%d %d", &n, &m); build(1, 1, n); while (m--) { int op, x, y; scanf("%d", &op); if (op == 1) { scanf("%d", &x); if (tr[1].ans < x) puts("0"); else { int l = query(1, x); printf("%d\n", l); change(1, l, l + x - 1, 1); // 1表示入住 } } else { scanf("%d %d", &x, &y); change(1, x, x + y - 1, 0); // 0表示退房 } } return 0; }
主要修复的地方
tag标记与区间赋值逻辑对齐,避免结构体整体替换造成信息丢失build初始化保证和题目初始状态一致pushdown明确左右子区间同步tag并更新lmax/rmax/ans- 递归更新和查询区间时严格用节点的
l/r作为判断依据
这样保持了你的原思路——用线段树维护最大连续空房间,并且懒标记处理入住和退房,查找时优先返回最小的房间号。
如果你愿意,我可以帮你画一个这个线段树每个节点值随操作变化的动态过程图,这样你就能直观理解 pushup/pushdown 是怎么保证信息正确的。这样会让你更快掌握这种题型。你要看吗
-
- 1