☆【線段樹】動態最值

有一个包含n个元素的数组,要求实现以下操作:

DELETE k:删除位置k上的数。右边的数往左移一个位置。

QUERY i j:查询位置i~j上所有数的最小值和最大值。

例如有10个元素:

位置

1

2

3

4

5

6

7

8

9

10

元素

1

5

2

6

7

4

9

3

1

5

QUERY 2 8的结果为2 9。依次执行DELETE 3和DELETE 6(注意这时删除的是原始数组的元素7)后数组变为:

位置

1

2

3

4

5

6

7

8

元素

1

5

6

7

4

3

1

5

QUERY 2 8的结果为1 7。

 

【输入】

输入文件minmax.in第一行包含两个数n, m,表示原始数组的元素个数和操作的个数。第二行包括n个数,表示原始数组。以下m行,每行格式为1 k或者2 i j,其中第一个数为1表示删除操作,为2表示询问操作。

 

【输出】

输出文件minmax.out对每个询问操作输出一行,包括两个数,表示该范围内的最小值和最大值。

 

【样例输入】

10 4

1 5 2 6 7 4 9 3 1 5

2 2 8

1 3

1 6

2 2 8

 

【样例输出】

2 9

1 7

 

【限制】

50%的数据满足1<=n, m<=104,删除操作不超过100个

100%的数据满足1<=n, m<=106, 1<=m<=106

对于所有的数据,数组中的元素绝对值均不超过109


一道較難處理的線段樹。

線段樹對應的區間是靜態的,但出現題目中所述情況(即要不停地刪除節點),應該怎麼辦呢?

方法是將絕對位置變為相對位置!

用tree[p].Sum表示該節點中實際剩餘節點總數,那麼就可以很輕鬆地把相對位置維護出來了。
Accode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>

using std::min;
using std::max;

const char fi[] = "minmax.in";
const char fo[] = "minmax.out";
const int maxN = 1000010;
const int MAX = 0x3fffffff;
const int MIN = -MAX;

struct SegTree
{int L, R, lc, rc, Min, Max, Sum; };

SegTree tree[maxN << 1];
int a[maxN];
int n, N, tot;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
  
  void update(SegTree *ths, SegTree
    *lc, SegTree *rc)
  {
    ths -> Sum = lc -> Sum + rc -> Sum;
    ths -> Min = min(lc -> Min, rc -> Min);
    ths -> Max = max(lc -> Max, rc -> Max);
  }
  
  void Build(int L, int R)
  {
    int Now = ++tot;
    tree[Now].L = L;
    tree[Now].R = R;
    tree[Now].Sum = 1;
    if (L == R)
    {
      tree[Now].Min = tree[Now].Max = a[L];
      return;
    }
    if (L < R)
    {
      int Mid = (L + R) >> 1;
      tree[Now].lc = tot + 1;
      Build(L, Mid);
      tree[Now].rc = tot + 1;
      Build(Mid + 1, R);
    }
    update(tree + Now, tree + tree[Now].lc,
      tree + tree[Now].rc);
  } //建樹。
  
  void Del(int Now, int p)
  {
    if (!(--tree[Now].Sum))
    {
      tree[Now].Max = MIN;
      tree[Now].Min = MAX;
      return;
    }	//將該區間的節點總數減1,
	//若該區間的節點總數為0,
	//那麼就將它的Min和Max賦為一個不可能的值。
    if (p <= tree[tree[Now].lc].Sum)
      Del(tree[Now].lc, p);
	//p不超過左子樹的節點總數,則p在左子樹,
	//繼續遍曆左子樹,相對位置不變。
    else Del(tree[Now].rc,
      p - tree[tree[Now].lc].Sum);
	//否則p在右子樹,繼續遍曆右子樹,
	//相對位置為p減去左子樹的節點數。
    update(tree + Now, tree + tree[Now].lc,
      tree + tree[Now].rc);
  } //在此區間中,刪除第p個節點。
  
  SegTree Query(int Now, int L, int R)
  {
    if (R - L + 1 == tree[Now].Sum)
      return tree[Now];
	//若實際總數剛好等於L到R包含的節點個數,
	//則直接返回此節點的信息。
    if (R <= tree[tree[Now].lc].Sum
      && tree[tree[Now].lc].Sum)
      return Query(tree[Now].lc, L, R);
	//若在左子樹且左子樹中有節點則返回左子樹的信息。
    if (L > tree[tree[Now].lc].Sum
      && tree[tree[Now].rc].Sum)
      return Query(tree[Now].rc,
        L - tree[tree[Now].lc].Sum,
        R - tree[tree[Now].lc].Sum);
	//若在右子樹且右子樹中有節點則返回右子樹的信息,
	//注意相對位置的改變。
    SegTree ans, lc = Query(tree[Now].lc,
      L, tree[tree[Now].lc].Sum),
      rc = Query(tree[Now].rc, 1,
      R - tree[tree[Now].lc].Sum);
	//若跨左右兩棵子樹則二分繼續查找,
	//注意此時相對位置與上不同。
    update(&ans, &lc, &rc);
    return ans;
  } //在此區間中,查詢從第L個到第R個子區間的Min,Max值。
  
  void work()
  {
    scanf("%d%d", &n, &N);
    for (int i = 1; i < n + 1; ++i)
      scanf("%d", a + i);
    tot = 0;
    Build(1, n);
    for (; N; --N)
    {
      int p, x, y;
      scanf("%d", &p);
      switch (p)
      {
        case 1 :
        {
          scanf("%d", &x);
          Del(1, x);
          break;
        }
        case 2 :
        {
          scanf("%d%d", &x, &y);
          if (x > y) std::swap(x, y);
          SegTree tmp = Query(1, x, y);
          printf("%d %d\n", tmp.Min,
            tmp.Max);
          break;
        }
      }
    }
  }
  
int main()
{
  init_file();
  work();
  exit(0);
}

第二次做:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

const int maxN = 1000010;
const int MAX = 0x3f3f3f3f;
const int MIN = ~MAX;

struct SegTree {int Min, Max, sz;};
SegTree ans, tr[maxN << 1];
int a[maxN], lc[maxN << 1], rc[maxN << 1];
int n, m, tot;

inline void update(SegTree &ths,
                   const SegTree &lc,
                   const SegTree &rc)
{
    ths.Min = min(lc.Min, rc.Min);
    ths.Max = max(lc.Max, rc.Max);
    ths.sz = lc.sz + rc.sz;
    return;
}

void Build(int L, int R)
{
    int Now = ++tot;
    if (L == R)
    {
        tr[Now].Max = tr[Now].Min = a[L];
        tr[Now].sz = 1;
        return;
    }
    int Mid = (L + R) >> 1;
    lc[Now] = tot + 1; Build(L, Mid);
    rc[Now] = tot + 1; Build(Mid + 1, R);
    update(tr[Now], tr[lc[Now]], tr[rc[Now]]);
    return;
}

void Del(int p, int x)
{
    if (!(--tr[p].sz))
    {tr[p].Min = MAX; tr[p].Max = MIN; return;}
    int Mid = tr[lc[p]].sz;
    if (x <= Mid) Del(lc[p], x);
    else Del(rc[p], x - Mid);
    update(tr[p], tr[lc[p]], tr[rc[p]]);
    return;
}

SegTree Query(int p, int L, int R)
{
    if (R - L + 1 == tr[p].sz) return tr[p];
    int Mid = tr[lc[p]].sz;
    if (R <= Mid && tr[lc[p]].sz) //此时左子树必须存在。
        return Query(lc[p], L, R);
    if (Mid < L && tr[rc[p]].sz) //此时右子树必须存在。
        return Query(rc[p], L - Mid, R - Mid);
    SegTree ans, _lc = Query(lc[p], L, Mid),
    _rc = Query(rc[p], 1, R - Mid);
    update(ans, _lc, _rc);
    return ans;
}

inline int getint()
{
    int res = 0; char tmp; bool sgn = 1;
    do tmp = getchar();
    while (!isdigit(tmp) && tmp != '-');
    if (tmp == '-') {sgn = 0; tmp = getchar();}
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return sgn ? res : -res;
} //注意输出数据有负数。

int main()
{
    freopen("minmax.in", "r", stdin);
    freopen("minmax.out", "w", stdout);
    n = getint(); m = getint();
    for (int i = 1; i < n + 1; ++i)
        a[i] = getint();
    Build(1, n);
    for (int x, y; m; --m)
    switch (getint())
    {
    case 1: Del(1, getint()); break;
    case 2:
        x =  getint(); y = getint();
        if (x > y) std::swap(x, y);
	//注意有x > y的情况。
        ans = Query(1, x, y);
        printf("%d %d\n", ans.Min, ans.Max);
        break;
    }
    return 0;
}

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看rEADME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看ReAdme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 、本项目3比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看ReAdme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通;、本项 3目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看ReAdme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值