【题目描述】
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;
}