线段树相关题目

线段树习题:
河南萌新联赛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,1n,q105
第二行到第 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 1xn
操作二: o p , L , R op,L,R op,L,R , 1 ≤ L ≤ R ≤ n 1 \leq L \leq R \leq n 1LRn

输出描述:

每次进行操作二后每行输出一个非负整数代表答案

样例:
在这里插入图片描述
题解有点摸不着头脑,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;
}

P3870 [TJOI2009] 开关

这个题目同样为区间修改,每次将一个区间的灯进行状态修改,将这个区间开着的灯关掉,关着的灯打开,可以将开灯状态看做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;
}

后序有题目会继续更新在这篇文章下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幻听嵩的留香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值