【線段樹】簡單題(easy)

有一个n个元素的数组,每个元素初始均为0。有m条指令,要么让其中一段连续序列数字反转——0变1,1变0(操作1),要么询问某个元素的值(操作2)。例如当n=20时,10条指令如下:

 

操作

回答

操作后的数组

1 1 10

N/A

11111111110000000000

2 6

1

11111111110000000000

2 12

0

11111111110000000000

1 5 12

N/A

11110000001100000000

2 6

0

11110000001100000000

2 15

0

11110000001100000000

1 6 16

N/A

11110111110011110000

1 11 17

N/A

11110111111100001000

2 12

1

11110111111100001000

2 6

1

11110111111100001000

 

【输入文件】

输入文件easy.in第一行包含两个整数nm,表示数组的长度和指令的条数,以下m行,每行的第一个数t表示操作的种类。若t=1,则接下来有两个数L, R(L<=R),表示区间[L,R]的每个数均反转;若t=2,则接下来只有一个数I,表示询问的下标。

 

【输出文件】

    每个操作2输出一行(非0即1),表示每次操作2的回答。

 

【样例】

easy.in

easy.out

20 10

1 1 10

2 6

2 12

1 5 12

2 6

2 15

1 6 16

1 11 17

2 12

2 6

1

0

0

0

1

1

 


 

【限制】

50%的数据满足:1<=n<=1,000,1<=m<=10,000

100%的数据满足:1<=n<=100,000,1<=m<=500,000

一道線段樹的入門題。
首先建樹(也可不建),建立根節點和子節點之間的關係。
然後依次讀入各個需要被翻轉的區間,二分翻轉,查詢的時候也二分查詢,詳細細節見程序中注釋。

Accode:

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

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

struct SegTree {int L, R, lc, rc, col; };
SegTree tree[maxN << 2];
int n ,m, tot;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
  
  void Build(int L, int R)
  {
    int Now = ++tot;
    tree[Now].L = L;
    tree[Now].R = R;
    tree[Now].col = 0;
    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);
    }
  }
  
  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; return; }
	//若該區間被完全覆蓋,則翻轉次數+1。
    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);
	//先訪問左子樹, 後訪問右子樹。
  }
  
  int Query(int Now, int p)
  {
    if (tree[Now].L == tree[Now].R
      && p == tree[Now].L)
      return tree[Now].col;
	//到大葉節點,則直接返回它的翻轉次數。
    int ans = tree[Now].col;
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (p <= Mid)
      return (ans += Query(tree[Now].lc, p)) & 1;
    if (p > Mid)
      return (ans += Query(tree[Now].rc, p)) & 1;
	//回溯將從業節點到總根節點的翻轉次數依次累加。
  }
  
  void work()
  {
    scanf("%d%d", &n, &m);
    tot = 0;
    Build(1, n);
    for (; m; --m)
    {
      int O, x, y;
      scanf("%d", &O);
      switch (O)
      {
        case 1 : //Flip the numbers.
          scanf("%d%d", &x, &y);
          Flip(1, x, y);
          break;
        case 2 : //Query the number.
          scanf("%d", &x);
          printf("%d\n", Query(1, x));
          break;
      }
    }
  }
  
int main()
{
  init_file();
  work();
  exit(0);
}

還有一個要超時的程序

將各個區間分為三種類型。
1) 全部為0,則顏色標記為0;
2) 全部為1,則顏色標記為1;
3) 01混合,則顏色標記為-1。

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

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

struct SegTree {int L, R, lc, rc, col; };
SegTree tree[maxN << 2];
int n ,m, tot;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
  
  void Build(int L, int R)
  {
    int Now = ++tot;
    tree[Now].L = L;
    tree[Now].R = R;
    tree[Now].col = 0;
    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);
    }
  }
  
  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)
     if (tree[Now].col > -1)
      {
        tree[Now].col = 1 - tree[Now].col;
        return;
      }
	//若為單色且被完全覆蓋,則直接翻轉。
    
    if (tree[Now].L < tree[Now].R)
    {
      if (tree[Now].col > -1)
        tree[tree[Now].lc].col =
          tree[tree[Now].rc].col =
          tree[Now].col;
	//若原來為單色,則標記向下傳。
      tree[Now].col = -1;
	//把它標記為單色。
      int Mid = (tree[Now].L + tree[Now].R) >> 1;
      Flip(tree[Now].lc, L, R);
      Flip(tree[Now].rc, L, R);
	//翻轉左右子區間。
      if (tree[tree[Now].lc].col > -1
        && tree[tree[Now].rc].col > -1
        && tree[tree[Now].lc].col ==
        tree[tree[Now].rc].col)
        tree[Now].col = tree[tree[Now].lc].col;
	//若翻轉後為單色,則改變標記。
    }
  }
  
  bool Query(int Now, int p)
  {

    if (tree[Now].col > -1) return tree[Now].col;
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (p <= Mid)
      return Query(tree[Now].lc, p);
    if (p > Mid)
      return Query(tree[Now].rc, p);
  }
  
  void work()
  {
    scanf("%d%d", &n, &m);
    tot = 0;
    Build(1, n);
    for (int i = 1; i < m + 1; ++i)
    {
      int O, x, y;
      scanf("%d", &O);
      switch (O)
      {
        case 1 : //Flip the numbers.
          scanf("%d%d", &x, &y);
          Flip(1, x, y);
          break;
        case 2 : //Query the number.
          scanf("%d", &x);
          printf("%d\n", Query(1, x));
          break;
      }
    }
  }

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值