线段树习题:
河南萌新联赛2024第(三)场:河南大学 E题 区间
题目描述:
在一个长度为 n n n的纸带上,初始时所有位置颜色为白色,现在要执行以下两种操作一共 q q q次
操作一:输入一个下标 x x x,你需要将位置 x x x的颜色翻转(白色变为黑色,黑色变为白色)
操作二; 输入两个正整数 L , R L , R L,R,你需要输出区间[ [ L , R ] [L,R] [L,R]中的连续的白色区间长度最大值
输入描述:
第一行输入两个正整数 n , q , 1 ≤ n , q ≤ 1 0 5 n,q,1 \le n,q \le 10^5 n,q,1≤n,q≤105
第二行到第 q + 1 q+1 q+1行,每行先给出一个正整数 o p op op,如果 o p = 1 op=1 op=1代表操作一, o p = 2 op=2 op=2代表操作二
输入格式如下:
操作一: o p , x op,x op,x 1 ≤ x ≤ n 1 \leq x \leq n 1≤x≤n
操作二: o p , L , R op,L,R op,L,R , 1 ≤ L ≤ R ≤ n 1 \leq L \leq R \leq n 1≤L≤R≤n
输出描述:
每次进行操作二后每行输出一个非负整数代表答案
样例:
题解有点摸不着头脑,c++类不是很熟,所以重新写了一份E题题解:
大致意思就是求区间L~R的最长白色区间,初始全为白色,每次操作一个位置,白变黑,黑变白。我们可以用0表示黑,1表示白。
接着,定义线段树节点结构node: int l, r, sz, ls, rs, sum;
其中ls是从l左边界开始的连续白色序列,如果l是个黑色的,那么,以6为左边界的区间的ls都为0
实现代码:
merge合并函数注意两个相连区间也可能会对sum值有影响
其他函数基本都差不多,把node这几个变量弄懂后,写起来就简单了
#include<bits/stdc++.h>
using namespace std;
#define lc p<<1
#define rc p<<1|1
const int N = 1e5;
struct node {
int l, r, sz, ls, rs, sum;
}t[N<<2];
node merge(node x, node y) {
node res;
res.l = x.l; res.r = y.r; res.sz = res.r - res.l + 1;
res.ls = x.ls == x.sz ? x.ls + y.ls : x.ls;
res.rs = y.rs == y.sz ? y.rs + x.rs : y.rs;
res.sum = max(x.sum, max(y.sum, x.rs + y.ls));
return res;
}
void pushup(int p) {
t[p] = merge(t[lc], t[rc]);
}
void build(int p, int l, int r) {
t[p] = {l, r, r-l+1, 1, 1, 1};
if(l == r) return;
int m = l + r >> 1;
build(lc, l, m);
build(rc, m+1, r);
pushup(p);
}
void modify(int p, int x) {
if(t[p].l == x && t[p].r == x) {
t[p].ls = t[p].rs = t[p].sum = 1 - t[p].sum;
return;
}
int m = t[p].l + t[p].r >> 1;
if(x <= m) modify(lc, x);
else modify(rc, x);
pushup(p);
}
node query(int p, int x, int y) {
if(t[p].l >= x && t[p].r <= y) return t[p];
int m = t[p].l + t[p].r >> 1;
if(y <= m) return query(lc, x, y);
else if(x > m) return query(rc, x, y);
return merge(query(lc, x, y), query(rc, x, y));
}
int main() {
int n, q; cin >> n >> q;
build(1, 1, n);
for(int i = 1; i <= q; i++) {
int op; cin >> op;
if(op == 1) {
int x; cin >> x;
modify(1, x);
}
else {
int L, R; cin >> L >> R;
cout << query(1, L, R).sum << '\n';
}
}
return 0;
}
这个题目同样为区间修改,每次将一个区间的灯进行状态修改,将这个区间开着的灯关掉,关着的灯打开,可以将开灯状态看做1,关灯状态看做0,整个修改就是将区间状态进行异或1操作,初始时,灯状态都是0,树结点加入懒标记xr,用来记录是0还是1,是0就不用修改,是1则要对子区间进行传递修改值
#include<iostream>
#include<vector>
using namespace std;
#define lc p<<1
#define rc p<<1|1
const int N = 1e5 + 10;
int n, m;
struct node{
int l, r, sz, sum, xr;
}t[N<<2];
void pushup(int p) {
t[p].sum = t[lc].sum + t[rc].sum;
}
void pushdown(int p) {
if(t[p].xr) {
t[lc].sum = t[lc].sz - t[lc].sum;
t[rc].sum = t[rc].sz - t[rc].sum;
t[lc].xr ^= 1;
t[rc].xr ^= 1;
}
t[p].xr = 0;
}
void build(int p, int l, int r) {
t[p] = {l, r, r-l+1, 0, 0};
if(l == r) return;
int m = l + r >> 1;
if(l <= m) build(lc, l, m);
if(r > m) build(rc, m+1, r);
pushup(p);
}
void change(int p, int x, int y) {
if(t[p].l >= x && t[p].r <= y) {
t[p].sum = t[p].sz - t[p].sum;
t[p].xr ^= 1;
return;
}
pushdown(p);
int m = t[p].l + t[p].r >> 1;
if(x <= m) change(lc, x, y);
if(y > m) change(rc, x, y);
pushup(p);
}
int query(int p, int x, int y) {
if(t[p].l >= x && t[p].r <= y) return t[p].sum;
int m = t[p].l + t[p].r >> 1;
pushdown(p);
int ans = 0;
if(x <= m) ans += query(lc, x, y);
if(y > m) ans += query(rc, x, y);
return ans;
}
int main() {
cin >> n >> m;
build(1, 1, n);
for(int i = 1; i <= m; i++) {
int op, a, b; cin >> op >> a >> b;
if(!op) {
change(1, a, b);
}
else {
cout << query(1, a, b) << '\n';
}
}
return 0;
}
后序有题目会继续更新在这篇文章下。