有一个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第一行包含两个整数n,m,表示数组的长度和指令的条数,以下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);
}