题目描述

对一家有 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 条评论

  • @ 2026-3-18 9:39:46

    • @ 2025-8-28 14:34:02

      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;
      }
      
      
      • @ 2025-8-27 21:32:28

        我看了你的代码,整体思路是标准的线段树维护区间最长连续空房间的方案,pushup/pushdown 逻辑是对的,不过你现在的 buildchangestruct 初始化存在几个问题,导致它和原题思路有些出入。我帮你按原思路改了一版,保留了核心写法,同时修正了几个细节:


        改动要点

        1. 结构体成员顺序和初始化
          你的 Tree 结构体里 lenanslmaxrmax 的赋值有些乱,构造时建议显式写出全部成员,避免 {} 顺序错乱。

        2. build 函数
          原思路里初始状态是所有房间为空,所以:

          • lmax = rmax = ans = len
          • tag = 0 表示空房(或用 -1 表示无懒标记)
        3. pushup
          保持原逻辑:lmax 要考虑左子区间全空时累加右子区间的 lmaxrmax 同理。

        4. pushdown
          注意这里 lazy 标记如果是 0 表示“全空”,1 表示“全占”,-1 表示“无标记”。你原本的初始化 tag = -1 是对的,但在 build 里要保证一致。

        5. change
          原思路:直接整段覆盖成全空或全占,anslmaxrmax 对应赋 0 或 len。不要用构造体整体替换(容易丢掉 tag 语义),建议逐个成员赋值。

        6. 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 是怎么保证信息正确的。这样会让你更快掌握这种题型。你要看吗

        • @ 2025-8-27 21:31:50
          #include <stdio.h>
          
          int main() {
              printf("HURRY UP!");
              return 0;
          }
          
          
          • 1