☆【線段樹】序列操作

【题目描述】
lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:
0 a b 把[a, b]区间内的所有数全变成0
1 a b 把[a, b]区间内的所有数全变成1
2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0
3 a b 询问[a, b]区间内总共有多少个1
4 a b 询问[a, b]区间内最多有多少个连续的1
对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?
【输入】
   输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目
   第二行包括n个数,表示序列的初始状态
   接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b<n)表示对于区间[a, b]执行标号为op的操作
【输出】
   对于每一个询问操作,输出一行,包括1个数,表示其对应的答案
【样例输入】
   10 10
   0 0 0 1 1 0 1 0 1 1
   1 0 2
   3 0 5
   2 2 2
   4 0 4
   0 3 6
   2 3 7
   4 2 8
   1 0 5
   0 5 6
   3 3 9
【样例输出】
   5
   2
   6
   5
【数据范围】
   对于30%的数据,1<=n, m<=1000
   对于100%的数据,1<=n, m<=100000

一道較為複雜的線段樹。

題目很清晰,但涉及到各種操作,較為複雜。

用tree[p].col表示該區間的顏色:col = 1則全為1、col = 0則全為0、col = -1則不全為1;
tree[p].maxL表示從該區間左端開始的最大連續1的個數;
tree[p].maxR表示從該區間右端開始的最大連續1的個數;
tree[p].Sum表示該區間1的總個數;
tree[p].Max表示該區間的最大連續1的個數。

具體細節見程序注釋。

Accode:

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

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

const char fi[] = "operation.in";
const char fo[] = "operation.out";
const int maxN = 100010;
const int MAX = 0x3fffff00;
const int MIN = -MAX;

struct SegTree
{
  int L, R, lc, rc, col,
    maxL, maxR, Max, Sum;
};

SegTree tree[maxN << 2];
bitset <maxN> tmp;
int n, m, tot;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }

  void update(SegTree *ths, SegTree
    *lc, SegTree *rc)
  {
    ths -> maxL = lc -> maxL;
	//只在左子樹。
    if (lc -> col == 1)
      ths -> maxL = max(ths -> maxL,
        lc -> Sum + rc -> maxL);
	//若左子樹全為1,則有這種情況存在。
	//以上幾行更新maxL。
    ths -> maxR = rc -> maxR;
	//只在右子樹。
    if (rc -> col == 1)
      ths -> maxR = max(ths -> maxR,
        rc -> Sum + lc -> maxR);
	//若右子樹全為1,則有這種情況存在。
	//以上幾行更新maxR。
    ths -> Max = max(max(lc -> Max, rc -> Max),
      lc -> maxR + rc -> maxL);
	//更新Max的值(三種情況:
	//只在左子樹,只在右子樹和
	//跨左右兩棵子樹)。
    ths -> Sum = lc -> Sum + rc -> Sum;
	//更新Sum的值(直接將左右子樹的
	//Sum值相加即可)。
    ths -> col = -1;
    if (lc -> col == -1 || rc -> col == -1)
      return;
    if (lc -> col == rc -> col)
      ths -> col = lc -> col;
	//更新col的值,當且僅當左右子樹都為
	//單色且顏色相等時,ths為單色。
  }

  void Set(SegTree *ths)
  {
    ths -> maxL = ths -> maxR = ths
      -> Max = ths -> Sum =
      ths -> R - ths -> L + 1;
    ths -> col = 1;
  } //將ths所在區間置1。

  void Reset(SegTree *ths)
  {
    ths -> maxL = ths -> maxR = ths -> Max
      = ths -> Sum = ths -> col = 0;
  } //將ths所在區間置0。

  void Flip(SegTree *ths)
  {
    ths -> maxL = ths -> maxR = ths
      -> Max = ths -> Sum = ths -> R
      - ths -> L + 1 - ths -> Sum;
    ths -> col ^= 1;
  } //將ths所在區間翻轉。

  void Build(int L, int R)
  {
    int Now = ++tot;
    tree[Now].L = L;
    tree[Now].R = R;
    if (L == R)
    {
      tree[Now].maxL = tree[Now].maxR
        = tree[Now].Sum = tree[Now].Max
        = tree[Now].col = tmp.test(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 Passdown(SegTree *ths, SegTree
    *lc, SegTree *rc)
  {
    lc -> col = rc -> col = ths -> col;
	//左右子樹的顏色都未ths的顏色。
    lc -> maxL = lc -> maxR = lc -> Max
      = lc -> Sum = (ths -> col == 1) ?
      (lc -> R - lc -> L + 1) : 0;
	//修改左子樹的所有標記。
    rc -> maxL = rc -> maxR = rc -> Max
      = rc -> Sum = (ths -> col == 1) ?
      (rc -> R - rc -> L + 1) : 0;
	//修改右子樹的所有標記。
  } //標記向下傳(此時ths應為單色)。

  void Set(int Now, int L, int R)
  {
    if (L > tree[Now].R || R < tree[Now].L
      || tree[Now].col == 1)
      return;
	//若非法情況或該區間的顏色已經為1,則直接退出。
    if (L <= tree[Now].L && R >= tree[Now].R)
      {Set(tree + Now); return; }
	//若該區間被完全覆蓋則直接置1。
    if (tree[Now].col > -1)
    {
      Passdown(tree + Now, tree +
        tree[Now].lc, tree + tree[Now].rc);
	//若tree[Now]為單色,則標記向下傳。
      tree[Now].col = -1;
	//將tree[Now]置為多色。
    }
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (L <= Mid) Set(tree[Now].lc, L, R);
	//包含左子樹則遍曆左子樹。
    if (Mid < R) Set(tree[Now].rc, L, R);
	//包含右子樹則遍曆右子樹。
    update(tree + Now, tree +
      tree[Now].lc, tree + tree[Now].rc);
  }

  void Reset(int Now, int L, int R)
  {
    if (L > tree[Now].R || R < tree[Now].L
      || tree[Now].col == 0)
      return;
	//若非法情況或該區間的顏色已經為0,則直接退出。
    if (L <= tree[Now].L && R >= tree[Now].R)
      {Reset(tree + Now); return; }
	//若該區間被完全覆蓋則直接置0。
    if (tree[Now].col > -1)
    {
      Passdown(tree + Now, tree +
        tree[Now].lc, tree + tree[Now].rc);
	//若tree[Now]為單色,則標記向下傳。
      tree[Now].col = -1;
	//將tree[Now]置為多色。
    }
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (L <= Mid) Reset(tree[Now].lc, L, R);
	//包含左子樹則遍曆左子樹。
    if (Mid < R) Reset(tree[Now].rc, L, R);
	//包含右子樹則遍曆右子樹。
    update(tree + Now, tree +
      tree[Now].lc, tree + tree[Now].rc);
  }

  void Flip(int Now, int L, int R)
  {
    if (L > tree[Now].R || R < tree[Now].L)
      return; //若非法情況則退出。
    if (L <= tree[Now].L && R >= tree[Now].R
      && tree[Now].col > -1)
      {Flip(tree + Now); return; }
	//若該區間被完全覆蓋且為單色,則直接翻轉。
    if (tree[Now].col > -1)
    {
      Passdown(tree + Now, tree +
        tree[Now].lc, tree + tree[Now].rc);
	//若tree[Now]為單色,則標記向下傳。
      tree[Now].col = -1;
	//將tree[Now]置為多色。
    }
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (L <= Mid) Flip(tree[Now].lc, L, R);
	//若包含左子樹則遍曆左子樹。
    if (Mid < R) Flip(tree[Now].rc, L, R);
	//若包含右子樹則遍曆右子樹。
    update(tree + Now, tree +
      tree[Now].lc, tree + tree[Now].rc);
  }

  SegTree Query(int Now, int L, int R)
  {
    if (L <= tree[Now].L && R >= tree[Now].R)
      return tree[Now];
	//若該區間被完全覆蓋,則直接返回該節點的信息。
    if (tree[Now].col > -1)
      Passdown(tree + Now, tree +
        tree[Now].lc, tree + tree[Now].rc);
	//萬分注意這裡也要標記向下傳。
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (R <= Mid) return Query(tree[Now].lc, L, R);
	//若完全被左子樹覆蓋則返回左子樹的信息。
    if (Mid < L) return Query(tree[Now].rc, L, R);
	//若完全被右子樹覆蓋則返回右子樹的信息。
    SegTree ans, lc = Query(tree[Now].lc, L, R),
      rc = Query(tree[Now].rc, L, R);
	//若跨左右兩個子樹則二分繼續查找。
    update(&ans, &lc, &rc);
    return ans; //注意返回結果。
  }

  void work()
  {
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n + 1; ++i)
      {int x; scanf("%d", &x); if (x) tmp.set(i); }
    tot = 0;
    Build(1, n);
    for (; m; --m)
    {
      int op, L, R;
      scanf("%d%d%d", &op, &L, &R);
      if (L > R) std::swap(L, R);
	//排除非法狀況。
      ++L; ++R;
	//由於題目數據採用零下標起始,則LR指針自加1。
      switch (op)
      {
        case 0: Reset(1, L, R); break;
	//將[L, R]置0。
        case 1: Set(1, L, R); break;
	//將[L, R]置1。
        case 2: Flip(1, L, R); break;
	//將[L, R]翻轉。
        case 3:
          printf("%d\n", Query(1, L, R).Sum);
	//查詢[L, R]的1的總數。
          break;
        case 4:
          printf("%d\n", Query(1, L, R).Max);
	//查詢[L, R]的最大連續1的個數。
          break;
      }
    }
  }

int main()
{
  init_file();
  work();
  exit(0);
}

第二次做:

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

const char fi[] = "operation.in";
const char fo[] = "operation.out";
const int maxN = 100010;
const int MAX = 0x3f3f3f3f;
const int MIN = ~MAX;

struct SegTree
{int L, R, lc, rc, col, cnt, maxL, maxR, Max;};

SegTree tr[maxN << 1];
int a[maxN], n, m, tot;

inline void update(SegTree &ths,
                   const SegTree &lc,
                   const SegTree &rc)
{
    ths.col = (lc.col == rc.col) ? lc.col : -1;
    ths.cnt = lc.cnt + rc.cnt;
    ths.maxL = lc.maxL; ths.maxR = rc.maxR;
    if (lc.col == 1)
        ths.maxL = max(ths.maxL, lc.cnt + rc.maxL);
    if (rc.col == 1)
        ths.maxR = max(ths.maxR, lc.maxR + rc.cnt);
    ths.Max = max(max(lc.Max, rc.Max),
                  lc.maxR + rc.maxL);
    return;
}

inline void push_down(int &p)
{
    tr[tr[p].lc].col = tr[tr[p].rc].col = tr[p].col;
    tr[tr[p].lc].cnt = tr[tr[p].lc].maxL
    = tr[tr[p].lc].maxR = tr[tr[p].lc].Max
    = tr[p].col ? (tr[tr[p].lc].R + 1 -
                   tr[tr[p].lc].L) : 0;
    tr[tr[p].rc].cnt = tr[tr[p].rc].maxL
    = tr[tr[p].rc].maxR = tr[tr[p].rc].Max
    = tr[p].col ? (tr[tr[p].rc].R + 1 -
                   tr[tr[p].rc].L) : 0;
    return;
}

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

void Reset(int p, int L, int R)
{
    if (L <= tr[p].L && R >= tr[p].R)
    {
        tr[p].col = tr[p].cnt = tr[p].maxL
        = tr[p].maxR = tr[p].Max = 0;
        return;
    }
    if (tr[p].col != -1) push_down(p);
    int Mid = (tr[p].L + tr[p].R) >> 1;
    if (L <= Mid) Reset(tr[p].lc, L, R); //
    if (Mid < R) Reset(tr[p].rc, L, R); //
    update(tr[p], tr[tr[p].lc], tr[tr[p].rc]);
    return;
}

void Set(int p, int L, int R)
{
    if (L <= tr[p].L && R >= tr[p].R)
    {
        tr[p].col = 1;
        tr[p].cnt = tr[p].maxL
        = tr[p].maxR = tr[p].Max
        = tr[p].R - tr[p].L + 1;
        return;
    }
    if (tr[p].col != -1) push_down(p);
    int Mid = (tr[p].L + tr[p].R) >> 1;
    if (L <= Mid) Set(tr[p].lc, L, R); //
    if (Mid < R) Set(tr[p].rc, L, R); //
    update(tr[p], tr[tr[p].lc], tr[tr[p].rc]);
    return;
}

void Flip(int p, int L, int R)
{
    if (L <= tr[p].L && R >= tr[p].R
        && tr[p].col == 0)
    {Set(p, L, R); return;}
    if (L <= tr[p].L && R >= tr[p].R
        && tr[p].col == 1)
    {Reset(p, L, R); return;}
    if (tr[p].col != -1) push_down(p);
    int Mid = (tr[p].L + tr[p].R) >> 1;
    if (L <= Mid) Flip(tr[p].lc, L, R); //
    if (Mid < R) Flip(tr[p].rc, L, R); //
    update(tr[p], tr[tr[p].lc], tr[tr[p].rc]);
    return;
}

SegTree Query(int p, int L, int R)
{
    if (L <= tr[p].L && R >= tr[p].R) return tr[p];
    if (tr[p].col != -1) push_down(p);
    int Mid = (tr[p].L + tr[p].R) >> 1;
    if (Mid < L) return Query(tr[p].rc, L, R);
    if (R <= Mid) return Query(tr[p].lc, L, R);
    SegTree ans, lc = Query(tr[p].lc, L, R),
    rc = Query(tr[p].rc, L, R);
    update(ans, lc, rc); return ans;
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

int main()
{
    freopen("operation.in", "r", stdin);
    freopen("operation.out", "w", stdout);
    n = getint(); m = getint();
    for (int i = 0; i < n; ++i) a[i] = getint();
    Build(0, n - 1);
    for (int L, R; m; --m)
    {
        switch (getint())
        {
        case 0:
            L = getint(); R = getint();
            Reset(1, L, R); break;
        case 1:
            L = getint(); R = getint();
            Set(1, L, R); break;
        case 2:
            L = getint(); R = getint();
            Flip(1, L, R); break;
        case 3:
            L = getint(); R = getint();
            printf("%d\n", Query(1, L, R).cnt);
            break;
        case 4:
            L = getint(); R = getint();
            printf("%d\n", Query(1, L, R).Max);
            break;
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值