【专题训练】线段树
发表于|更新于
|总字数:1.7k|阅读时长:8分钟|浏览量:|
两年前就在为写线段树苦恼,两年后居然还在被线段树困扰!!于是在这个被 bct 出的超绝 1 数据结构 + 3 DP(含 2 期望)的模拟赛整疯的一天,我决定进行线段树专练。
题单链接:线段树练习
P5057 [CQOI2006] 简单题
题目链接
题意简述
数组 $a$ 为布尔类型,有 $n$ 个元素,初始全为 $0$ ,进行 $m$ 次下列任一操作,输出操作 2 的结果:
- 让其中一段连续序列数字反转。
- 询问某个元素的值。
练习过程
对照着自己之前 P3372 的代码写,考虑主要是把计算改成 ^1 的操作,有下面几个问题不是很清楚:
- 需不需要
lazy 。
- 非叶子节点的
data 内应填什么值。
- 需不需要
pushup。
query 应返回什么值。
所以先凭着感觉走了。。。
第一版 CODE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| const int N = 1e5 + 29;
int n, m, k; int a[N]; struct tree { int l, r; int data; } tr[N << 2];
void pushdown(int u) { if (!tr[u].data) return; tr[u << 1].data ^= 1; tr[u << 1 | 1].data ^= 1; }
void build(int u, int l, int r) { tr[u].l = l; tr[u].r = r; if (l == r) tr[u].data = 0; int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); }
void modify(int u, int l, int r) { if (tr[u].r < l || tr[u].l > r) return; if (tr[u].l >= l && tr[u].r <= r) { tr[u].data ^= 1; return; } pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify(u << 1, l, mid); if (r > mid) modify(u << 1 | 1, mid + 1, r); }
void query(int u, int x) { if (u == x) return; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) query(u << 1, x); else query(u << 1 | 1, x); }
void tbcsolve() { cin >> n >> m; build(1, 1, n); rep (i, 1, m) { int t; cin >> t; if (t == 1) { int l, r; cin >> l >> r; modify(1, l, r); } else { int x; cin >> x; query(1, x); cout << tr[x].data << endl; } } }
|
但是注意到此代码是没有输出的……
经过编译运行发现代码卡在了 build 时,静态查错发现是 l == r 时没有 return。
`build` 更正
1 2 3 4 5 6 7 8 9
| void build(int u, int l, int r) { tr[u].l = l; tr[u].r = r; tr[u].data = 0; if (l == r) return; int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); }
|
但是仍然没有输出……此处进行 1 操作没有问题,卡在了 query。
debug 发现在 pushdown 的时候存在异常,但是没看出来是什么问题,于是求助了万能的 hm2ns,发现是我判的太阴间导致对叶子节点 pushdown 了。
好吧是我的 query 实在是写的太阴间了……
`query` 更正
1 2 3 4 5 6 7
| bool query(int u, int x) { if (tr[u].l == tr[u].r) return tr[u].data; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) return query(u << 1, x); else return query(u << 1 | 1, x); }
|
超级好消息:跑出结果啦!!!
But 这跑的也不对啊……
调了四个多小时……终于发现了……
是这个 modify 里面的递归,l 和 r 要原封不动地放进去递归。
但是 sheep32768 说我之前那样写也没什么问题。可改了之后就过了……
其实我也不理解为什么不能用 mid
`modify` 更正
1 2 3 4 5 6 7 8 9 10 11
| void modify(int u, int l, int r) { if (tr[u].r < l || tr[u].l > r) return; if (tr[u].l >= l && tr[u].r <= r) { tr[u].data ^= 1; return; } pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify(u << 1, l, r); if (r > mid) modify(u << 1 | 1, l, r); }
|
AC 记录
AC 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| const int N = 1e5 + 29;
int n, m, k; int a[N]; struct tree { int l, r; int data; } tr[N << 2];
void pushdown(int u) { if (!tr[u].data) return; tr[u << 1].data ^= 1; tr[u << 1 | 1].data ^= 1; tr[u].data = 0; }
void build(int u, int l, int r) { tr[u].l = l; tr[u].r = r; tr[u].data = 0; if (l == r) return; int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); }
void modify(int u, int l, int r) { if (tr[u].r < l || tr[u].l > r) return; if (tr[u].l >= l && tr[u].r <= r) { tr[u].data ^= 1; return; } pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify(u << 1, l, r); if (r > mid) modify(u << 1 | 1, l, r); }
bool query(int u, int x) { if (tr[u].l == tr[u].r) return tr[u].data; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) return query(u << 1, x); else return query(u << 1 | 1, x); }
void tbcsolve() { cin >> n >> m; build(1, 1, n); rep (i, 1, m) { int t; cin >> t; if (t == 1) { int l, r; cin >> l >> r; modify(1, l, r); } else { int x; cin >> x; cout << query(1, x) << endl; } } }
|
P4588 [TJOI2018] 数学计算
题目链接
题意简述
有一个数 $x$ ,进行 $Q$ 次操作,操作有两种类型:
1 m :将 $x$ 的值乘上 $m$ ,并输出 $x \mod M$
2 pos :将 $x$ 的值除以第 $pos$ 次操作所乘的值,并输出 $x \mod M$
练习过程
一开始并没看出来是线段树,经过一段时间的思考和猜测之后有了思路:
将每次 1 操作作为叶子节点的值,那么根节点即为 $x$ 的值;
对于 2 操作只需把 $pos$ 位置上的值改成 1 即可。
很幸运第一个代码就顺利通过了样例:
第一版也是最后一版 code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| const int N = 1e5 + 29;
int q, mod; struct tree { int l, r; int data; } tr[N << 2];
void pushup(int u) { tr[u].data = tr[u << 1].data * tr[u << 1 | 1].data % mod; }
void build(int u, int l, int r) { tr[u].l = l; tr[u].r = r; tr[u].data = 1; if (l == r) return; int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); }
void modify(int u, int x, int k) { if (tr[u].l == tr[u].r) { tr[u].data = k % mod; return; } int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) modify(u << 1, x, k); else modify(u << 1 | 1, x, k); pushup(u); }
void query(int u, int pos) { if (tr[u].l == tr[u].r) { tr[u].data = 1; return; } int mid = tr[u].l + tr[u].r >> 1; if (pos <= mid) query(u << 1, pos); else query(u << 1 | 1, pos); pushup(u); }
void tbcsolve() { cin >> q >> mod; build(1, 1, q); rep(i, 1, q) { int op; cin >> op; if (op == 1) { int x; cin >> x; modify(1, i, x); cout << tr[1].data % mod << endl; } else { int pos; cin >> pos; query(1, pos); cout << tr[1].data % mod << endl; } } }
signed main() { ios:: sync_with_stdio(0), cin.tie(0), cout.tie(0); int Trubiacy_ = 1; cin >> Trubiacy_; while (Trubiacy_--) { tbcsolve(); } return 0; }
|
啊啊啊?!!这居然就过了?!!【震惊.jpg
AC 记录
AC 代码
如上。