• POJ3667 Hotel


      原题传送:http://poj.org/problem?id=3667

      线段树。

      线段树题目做得还是太少了。这道题对新手来说是一道坎,虽然很容易得出思路,但是一细想会觉得挺恶心的,本想参考别人的AC代码,但一看就思密达了。只能静下心来,花几个小时甚至几天来做,还是会有很大收获的。

      这里有一份不错的结题报告:http://bbs.byr.cn/wForum/disparticle.php?boardName=ACM_ICPC&ID=18374&listType=1


      题目大意是有一个旅馆,其中有N个房间,初始化都为空,接下来有M个操作:
      操作1,形式为1 d,表示需要在旅馆中找到连续的d个房间,如果存在多个连续的d个房间,输出连续区间第一个元素最小的房间, 如果不存在连续的d个房间,输出0;
      操作2,形式为2 s d,表示在房间s至s+d-1的顾客退房,即这些房间重新置为空。
      可见题中都是对某个区间进行操作,我们可以很容易想到,这题应该使用线段树。
      线段树建树时间为O(n*logn),每次插入、查询时间为logn,因此全部复杂度为O(nlogn + mlogn),时间足够。

    具体做法:设1代表房间为空,0代表房间已住人
    线段树的节点存储以下的变量:
      int l, r, c;      //左端点,右端点,线段长度
      int lp, rp, mp;     //lp表示从线段左端点开始的连续1序列的长度,rp表示从线段右端点开始的连续1序列的长度,mp表示 线段中最大的连续1序列长度 
      int rd, md;           //rd代表从线段右端点开始连续1序列的左端点,md表示线段中最大连续1序列的左端点。
      int flag;               //如果为0,表示当前线段所有房间都已住;1表示所有房间都为空,2表示有房间为空,也有非空。

    线段树有两种操作,一种是插入操作insert,一种查询操作search
    Insert(int l, int r, int k, int f)
    其中[l,r]代表我们要进行操作的区间,k当前树节点编号,f=0代表插入(即将空房间置为1),f=1代表删除(即将满房间置为空房 间)。



    Insert大概流程如下:

    如果遇到节点t[k].l == l && t[k].r == r,则说明当前区间需要进行操作,根据参数f进行相应操作,返回;

    如果当前区间全为0或者全为1,则将其子区间也全部置为0或者1;
    //进行这个操作,是因为上一次对这个节点进行操作的时候,并没有对其子区间进行相应的操作,而现在我们需要使用子区间,则 先完成上一次应该对子区间进行的操作。

    根据l、r和t[k].l,t[k].r的关系,继续递归。

    递归完毕,更新当前节点的lp, rp, mp, rd, md
    lp和rp的更新都相对简单,要注意mp的更新,除了要考虑t[2*k].rp+t[2*k+1].lp,还需要考虑t[2*k].mp和t[2*k+1].mp,同时相应 更新md和rd。 结束。 


    search(int d, int k) //d代表需要的连续1区间长度,k代表当前节点
    先看节点k的左连续1区间lp,如果满足,直接返回左端点l;
    如果不满足,查找中间连续1区间mp,如果mp==d,则返回记录的起始点md,如果mp < d或者此时已到达叶节点,则返回-1,查找失 败。
    仍需要单独考虑mp > d的情况,因为mp记录是当前区间最长连续1区间的长度,并不代表这是我们要查找的答案,因为可能区间里仍 存在长度符合,而起始点更小的连续1区间,因此我们先进入左子树进行查找,如果失败,接着判断中间连续序列t[2*k].rp + t [2*k+1].lp >= d是否成立,成立则返回此序列起始点,如果不成立,进入右子树进行查找,返回结果。 结束。

    回到题目,对于每次输入的操作
    第一种操作,1 d,我们先进行search操作,如果能够找到长度不小于d的连续1区间,则返回最小的起始点,否则返回-1。如果找到
    这样一个区间,起始点为st,则进行插入操作insert(st, st+d-1, 1, 0),输出st;否则,输出0;
    第二种操作,2 s d,直接进行插入操作insert(s, s + d – 1, 1, 1)

    最后程序跑了600MS,时限是3000MS,说明算法还是比较合理的。

      (这个程序写得各种bug还各种过样例都不知道调试了多久。为什么代码写得这么长,几十次wa试试~)

    View Code
      1 #include <stdio.h>
      2 #include <string.h>
      3 #define lson (cur << 1)
      4 #define rson (cur << 1 | 1)
      5 #define N 50010
      6 
      7 struct node
      8 {
      9   int l, r, c;       // 左端点,右端点,线段长度
     10   int lp, rp, mp;    // lp表示从线段左端点开始的连续1序列的长度,rp表示从线段右端点开始的连续1序列的长度,mp表示线段中最大的连续1序列的长度。
     11   int rd, md;        // rd代表从线段右端点开始连续1序列的左端点,md表示线段中最大连续1序列的左端点。
     12   int flag;          // 如果为0,表示当前线段所有房间都已住;1表示所有房间都为空,2表示有房间为空,也有非空。
     13 }tree[N << 2];
     14 
     15 int n, m, lazy[N << 2];
     16 
     17 void pushup(int cur)
     18 {
     19     if(tree[lson].mp == tree[lson].c)  // lp: 表示从线段左端点开始的连续1序列的长度
     20         tree[cur].lp = tree[lson].c + tree[rson].lp;
     21     else
     22         tree[cur].lp = tree[lson].lp;
     23 
     24     if(tree[rson].mp == tree[rson].c)  // rp: 表示从线段右端点开始的连续1序列的长度
     25         tree[cur].rp = tree[rson].c + tree[lson].rp;
     26     else
     27         tree[cur].rp = tree[rson].rp;
     28 
     29     if(tree[rson].mp == tree[rson].c)  // rd: 从线段右端点开始连续1序列的左端点
     30         tree[cur].rd = (tree[lson].rp == 0 ? tree[rson].l : tree[lson].rd);
     31     else
     32         tree[cur].rd = tree[rson].rd;
     33 
     34     int m = 0;
     35     if(tree[lson].mp > m)              // mp: 表示线段中最大的连续1序列的长度
     36         m = tree[lson].mp, tree[cur].md = tree[lson].md;
     37     if(tree[lson].rp + tree[rson].lp > m)
     38         m = tree[lson].rp + tree[rson].lp, tree[cur].md = (tree[lson].rp == 0 ? tree[rson].l : tree[lson].rd);
     39     if(tree[rson].mp > m)
     40         m = tree[rson].mp, tree[cur].md = tree[rson].md;
     41     tree[cur].mp = m;
     42 
     43     if(tree[lson].flag == 2 || tree[rson].flag == 2 || tree[lson].flag != tree[rson].flag)
     44         tree[cur].flag = 2;
     45     else
     46         tree[cur].flag = tree[lson].flag;
     47 }
     48 
     49 void pushdown(int cur)
     50 {
     51     if(lazy[cur] == 0)
     52     {
     53         lazy[lson] = 0;
     54         tree[lson].flag = 0;
     55         tree[lson].lp = tree[lson].rp = tree[lson].mp = 0;
     56         lazy[rson] = 0;
     57         tree[rson].flag = 0;
     58         tree[rson].lp = tree[rson].rp = tree[rson].mp = 0;
     59         lazy[cur] = -1;
     60     }
     61     if(lazy[cur] == 1)
     62     {
     63         lazy[lson] = 1;
     64         tree[lson].flag = 1;
     65         tree[lson].rd = tree[lson].md = tree[lson].l;
     66         tree[lson].lp = tree[lson].rp = tree[lson].mp = tree[lson].r - tree[lson].l + 1;
     67         lazy[rson] = 1;
     68         tree[rson].flag = 1;
     69         tree[rson].rd = tree[rson].md = tree[rson].l;
     70         tree[rson].lp = tree[rson].rp = tree[rson].mp = tree[rson].r - tree[rson].l + 1;
     71         lazy[cur] = -1;
     72     }
     73 }
     74 
     75 void insert(int cur, int l, int r, int f)
     76 {
     77     if(tree[cur].l >= l && tree[cur].r <= r)
     78     {
     79         lazy[cur] = f;
     80         if(f == 1)
     81         {
     82             tree[cur].lp = tree[cur].rp = tree[cur].mp = tree[cur].r - tree[cur].l + 1;
     83             tree[cur].rd = tree[cur].md = tree[cur].l;
     84             tree[cur].flag = 1;
     85         }
     86         else if(f == 0)
     87         {
     88             tree[cur].lp = tree[cur].rp = tree[cur].mp = 0;
     89             tree[cur].flag = 0;
     90         }
     91         return ;
     92     }
     93     pushdown(cur);
     94     int mid = (tree[cur].l + tree[cur].r) >> 1;
     95     if(mid >= l)
     96         insert(lson, l, r, f);
     97     if(mid + 1 <= r)
     98         insert(rson, l, r, f);
     99     pushup(cur);
    100 }
    101 
    102 void search(int cur, int k, int &ans)
    103 {
    104     // 没有满足条件或已经到了叶子节点,或该区间无空位,失败返回
    105     if(tree[cur].flag == 0 || tree[cur].mp < k || tree[cur].l == tree[cur].r)
    106         return ;
    107 
    108     if(tree[cur].lp >= k)    // 左连续1区间lp,如果满足,直接返回左端点l
    109     {
    110         ans = tree[cur].l;
    111         return ;
    112     }
    113     if(tree[cur].mp == k)    // 中间连续1区间mp==d,则返回记录的起始点md
    114     {
    115         ans = tree[cur].md;
    116         return ;
    117     }
    118     pushdown(cur);
    119     if(tree[cur].mp > k)     // 先进入左子树进行查找,如果失败,接着判断中间连续序列t[2*k].rp + t[2*k+1].lp >= d是否成立,成立则返回此序列起始点,如果不成立,进入右子树进行查找,返回结果
    120     {
    121         if(tree[lson].mp >= k)  //  左子树满足条件,直接进入
    122             search(lson, k, ans);
    123         else  if(tree[lson].rp + tree[rson].lp >= k && tree[lson].rp != 0)  // 跨区间进入左区间
    124         {
    125             ans = tree[lson].rd;
    126             return ;
    127         }
    128         else   // 进入右区间
    129             search(rson, k, ans);
    130     }
    131     pushup(cur);
    132 }
    133 
    134 void build(int cur, int l, int r)
    135 {
    136     tree[cur].l = l, tree[cur].r = r, tree[cur].c = r - l + 1;
    137     lazy[cur] = -1;
    138     if(l == r)
    139     {
    140         tree[cur].lp = 1;
    141         tree[cur].rp = 1;
    142         tree[cur].mp = 1;
    143         tree[cur].flag = 1;
    144         tree[cur].rd = tree[cur].md = l;
    145         return ;
    146     }
    147     int mid = (l + r) >> 1;
    148     build(lson, l, mid);
    149     build(rson, mid + 1, r);
    150     pushup(cur);
    151 }
    152 
    153 int main()
    154 {
    155     int op, a, b, ans;
    156     while(scanf("%d%d", &n, &m) != EOF)
    157     {
    158         build(1, 1, n);
    159         while(m --)
    160         {
    161             scanf("%d", &op);
    162             if(op == 1)
    163             {
    164                 ans = 0;
    165                 scanf("%d", &a);
    166                 search(1, a, ans);
    167                 if(ans)  // !!!
    168                     insert(1, ans, ans + a - 1, 0);
    169                 printf("%d\n", ans);
    170             }
    171             else if(op == 2)
    172             {
    173                 scanf("%d%d", &a, &b);
    174                 insert(1, a, a + b - 1, 1);
    175             }
    176         }
    177     }
    178     return 0;
    179 }

     仅以此题纪念今日美好的中秋节

  • 相关阅读:
    iOS开发进阶
    iOS开发UI篇—Quartz2D使用(绘图路径)
    iOS开发UI篇—控制器的View的创建
    iOS开发UI篇—Quartz2D简单使用(三)
    iOS开发从入门到精通
    win7访问win10需要用户名密码
    win10启用guest来宾账户的教程
    2021 年终总结
    vue elementui 树形
    js递归生成树形结构
  • 原文地址:https://www.cnblogs.com/huangfeihome/p/2709255.html
Copyright © 2020-2023  润新知