HDOJ1540 - Tunnel Warfare 线段树区间合并

HDOJ 1540

题目大意:给定两个整数N,M, 其中N表示一共有N个村庄,M代表有M次操作,操作有以下:

1.    D x  销毁村庄x

2.    Q x  询问与村庄x相邻的村庄总数

3.    R     最近一次销毁的村庄得到重建

 

问题分析:

对于N个村庄,可以建立一颗线段树,维护最大连续区间长度,操作分析:

  1. 在线段树上找到x节点,并将x节点“销毁”(村庄x被销毁),然后分别更新与x节点(村庄x)相关联的节点值(这里指左右连续区间的长度)
  2. 对于询问操作,显然这里要求返回的是存在x的最大左右连续区间长度和
  3. 将村庄x恢复(具体到重置线段的覆盖标志),并更新与村庄x相关联的最大连续区间和

 

具体实现:

对于有N个村庄,可以建立区间为[1, N]的线段树,维护最大连续区间长度,节点里有这些信息需要维护:最大左连续区间长度,最大右连续区间长度,总对大连续区间长度,覆盖标志,每次PushUp都要将左右子区间进行合并,加入lazy-tag优化时间复杂度(简单来说就是将更新延迟到下一次询问或者需要更新的时候),对于R操作,因为该操作的对象是最近一次销毁的村庄,因此在每一次 D x 操作以后就将 x 进栈,等到 R 操作时就将栈顶弹出,于是操作对象就转移到栈顶元素

 

代码:

  1 #include <stack>

  2 #include <cstdio>

  3 using namespace std;

  4 

  5 #define lson l, m, rt<<1

  6 #define rson m+1, r, rt<<1|1

  7 

  8 const int maxn = 50000;

  9 

 10 char cmd[5];

 11 stack <int> st;

 12 int n, mNum, a;

 13 int lsum[maxn*3], rsum[maxn*3], sum[maxn*3], cover[maxn*3];

 14 

 15 int Max(int x, int y)

 16 {

 17     return (x>y ? x:y);

 18 }/* Max */

 19 

 20 void BuildTree(int l, int r, int rt)

 21 {

 22     cover[rt] = -1;

 23     lsum[rt] = rsum[rt] = sum[rt] = r-l+1;

 24     

 25     if (l == r)

 26         return ;

 27     

 28     int m = (l+r)>>1;

 29     BuildTree(lson);

 30     BuildTree(rson);

 31 }/* BuildTree */

 32 

 33 void PushDown(int rt, int k)

 34 {

 35     if (cover[rt] != -1)

 36     {

 37         cover[rt<<1] = cover[rt<<1|1] = cover[rt];

 38         lsum[rt<<1] = rsum[rt<<1] = sum[rt<<1] = cover[rt] ? 0:k-(k>>1);

 39         lsum[rt<<1|1] = rsum[rt<<1|1] = sum[rt<<1|1] = cover[rt] ? 0:(k>>1);

 40         cover[rt] = -1;

 41     }

 42 }/* PushDown */

 43 

 44 int Query(int p, int l, int r, int rt)

 45 {

 46     if (sum[rt]==0 || sum[rt]==r-l+1 || l==r)

 47         return sum[rt];

 48     

 49     PushDown(rt, r-l+1);

 50     

 51     int m = (l+r)>>1;

 52     if (p <= m)

 53     {

 54         if (p > m-rsum[rt<<1])

 55             return rsum[rt<<1]+Query(m+1, rson);

 56         else

 57             return Query(p, lson);

 58     }

 59     else

 60     {

 61         if (p <= m+lsum[rt<<1|1])

 62             return lsum[rt<<1|1]+Query(m, lson);

 63         else

 64             return Query(p, rson);

 65     }

 66 }/* Query */

 67 

 68 void PushUp(int rt, int k)

 69 {   /* 左右子区间合并 */ 

 70     lsum[rt] = lsum[rt<<1];

 71     rsum[rt] = rsum[rt<<1|1];

 72     

 73     if (lsum[rt] == k-(k>>1))

 74         lsum[rt] += lsum[rt<<1|1];

 75     if (rsum[rt] == (k>>1))

 76         rsum[rt] += rsum[rt<<1];

 77     

 78     sum[rt] = Max(rsum[rt<<1]+lsum[rt<<1|1], Max(sum[rt<<1], sum[rt<<1|1]));

 79 }/* PushUp */

 80 

 81 void UpData(int p, int c, int l, int r, int rt)

 82 {

 83     if (l == r)

 84     {

 85         lsum[rt] = rsum[rt] = sum[rt] = c ? 0:r-l+1;

 86         cover[rt] = c;

 87         

 88         return ;

 89     }/* End of If */

 90     

 91     PushDown(rt, r-l+1);

 92     

 93     int m = (l+r)>>1;

 94     if (p <= m)

 95         UpData(p, c, lson);

 96     else

 97         UpData(p, c, rson);

 98     

 99     PushUp(rt, r-l+1);

100 }/* UpData */

101 

102 int main()

103 {

104     while (~scanf("%d %d", &n, &mNum))

105     {

106         BuildTree(1, n, 1);

107         

108         while (!st.empty())

109             st.pop();

110         

111         for (int i=1; i<=mNum; ++i)

112         {

113             scanf("%s", cmd);

114             if (cmd[0] == 'Q')

115             {

116                 scanf("%d", &a);

117                 printf("%d\n", Query(a, 1, n, 1));

118             }

119             else if (cmd[0] == 'D')

120             {

121                 scanf("%d", &a);

122                 UpData(a, 1, 1, n, 1);

123                 st.push(a);

124             }

125             else

126             {

127                 a = st.top();

128                 st.pop();

129                 UpData(a, 0, 1, n, 1);

130             }

131         }/* End of For */

132     }/* End of While */    

133     

134     return 0;

135 }

 

你可能感兴趣的:(线段树)