线段树 lazy tag 小合集

这篇博客汇总了使用线段树lazy tag解决区间问题的基础和进阶实例,包括poj 3468、hiho 1078、poj 2777和hdu 4902等题目。作者探讨了tag的叠加性以及如何在不同场景下应用lazy tag,如区间加法、区间赋值和区间颜色统计等,提供了AC代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(这里的基础和进阶纯粹是根据PO主水平说的…大家意思意思看看就好Orz)

基础

poj 3468

题目链接

题意:
两种操作:
1. 对一段区间中的每一个数都加上一个值
2. 询问区间和

我学习 lazy tag 的第一道题,可以说是十分经典的模板题了,看了dalao的博客然后模仿着写了一遍
这道题比较重要的地方是 tag 的叠加性,在之后的另一道题(hdu 3911)里面会发现很容易忽略掉

AC代码如下:

#include <cstdio>
#define MAX 100010
typedef long long LL;
struct node {
    int l, r, len;
    LL tag, sum;
}tree[MAX * 4];
void push_down(int rt) {
    if (tree[rt].tag) {
        LL tag = tree[rt].tag;
        tree[rt * 2].tag += tag;
        tree[rt * 2 + 1].tag += tag;
        tree[rt * 2].sum += tag * tree[rt * 2].len;
        tree[rt * 2 + 1].sum += tag * tree[rt * 2 + 1].len;
        tree[rt].tag = 0;
    }
}
void lift_up(int rt) {
    tree[rt].sum = tree[rt * 2].sum + tree[rt * 2 + 1].sum;
}
void build(int rt, int l, int r) {
    tree[rt].l = l; tree[rt].r = r; tree[rt].len = r - l + 1;
    if (l == r) {
        scanf("%lld", &tree[rt].sum);
        return;
    }
    int mid = (l + r) / 2;
    build(rt * 2, l, mid);
    build(rt * 2 + 1, mid + 1, r);
    lift_up(rt);
}
void update(int rt, int l, int r, LL val) {
    if (l == tree[rt].l && r == tree[rt].r) {
        tree[rt].sum += (LL)tree[rt].len * val;
        tree[rt].tag += val;
        return;
    }
    push_down(rt);
    int mid = (tree[rt].l + tree[rt].r) / 2;
    if (r <= mid) update(rt * 2, l, r, val);
    else if (l > mid) update(rt * 2 + 1, l, r, val);
    else {
        update(rt * 2, l, mid, val);
        update(rt * 2 + 1, mid + 1, r, val);
    }
    lift_up(rt);
}
LL query(int rt, int l, int r) {
    if (l == tree[rt].l && r == tree[rt].r) {
        return tree[rt].sum;
    }
    push_down(rt);
    int mid = (tree[rt].l + tree[rt].r) / 2;
    if (r <= mid) return query(rt * 2, l, r);
    else if (l > mid) return query(rt * 2 + 1, l, r);
    else return query(rt * 2, l, mid) + query(rt * 2 + 1, mid + 1, r);
}
int main() {
//    freopen("3468.in", "r", stdin);
//    freopen("3468.out", "w", stdout);
    int n, q;
    scanf("%d%d\n", &n ,&q);
    build(1, 1, n);
    scanf("\n");
    for (int i = 0; i < q; ++i) {
        char ch; int l, r;
        scanf("%c", &ch);
        if (ch == 'Q') {
            scanf("%d%d\n", &l, &r);
            printf("%lld\n", query(1, l, r));
        }
        else {
            int val;
            scanf("%d%d%d\n", &l, &r, &val);
            update(1, l, r, val);
        }
    }
    return 0;
}

hiho 1078

题目链接

题意:
两种操作:
1. 将一段区间内的值修改为给定的值
2. 询问区间和

与第一道题基本相同,但比第一题更水些,tag都不需要叠加

AC代码如下:

#include <cstdio>
#define maxn 100010
#define lson ((rt) << 1)
#define rson ((rt) << 1 | 1)
inline int midi(int l, int r) { return (l + r) >> 1; }
struct node {
    int l, r, val, len, tag;
}tree[maxn * 4];
void lift_up(int rt) {
    tree[rt].val = tree[lson].val + tree[rson].val;
}
void push_down(int rt) {
    if (tree[rt].tag) {
        tree[lson].val = tree[rt].tag * tree[lson].len;
        tree[rson].val = tree[rt].tag * tree[rson].len;
        tree[lson].tag = tree[rson].tag = tree[rt].tag;
        tree[rt].tag = 0;
    }
}
void build(int rt, int l, int r) {
    tree[rt].l = l; tree[rt].r = r; tree[rt].len = r - l + 1;
    if (l == r) { scanf("%d", &tree[rt].val); return; }
    int mid = midi(l, r);
    build(lson, l, mid);
    build(rson, mid + 1, r);
    lift_up(rt);
}
void modify(int rt, int l, int r, int val) {
//    printf("%d %d %d\n", rt, l, r);

    if (tree[rt].l == l && tree[rt].r == r) {
        tree[rt].val = tree[rt].len * val;
//        printf("val : %d\n", tree[rt].len);
        tree[rt].tag = val;
        return;
    }
    push_down(rt);
    int mid = midi(tree[rt].l, tree[rt].r);
//    printf("mid %d\n", mid);
    if (r <= mid) modify(lson, l, r, val);
    else if (l > mid) modify(rson, l, r, val);
    else {
        modify(lson, l, mid, val);
        modify(rson, mid + 1, r, val);
    }
    lift_up(rt);
}
int query(int rt, int l, int r) {
    if (tree[rt].l == l && tree[rt].r == r) return tree[rt].val;
    push_down(rt);
    int mid = midi(tree[rt].l, tree[rt].r);
    if (r <= mid) return query(lson, l, r);
    else if (l > mid) return query(rson, l, r);
    else return query(lson, l, mid) + query(rson, mid + 1, r);
}
int main() {
//    freopen("1078.in", "r", stdin);
    int n, m;
    scanf("%d", &n);
    build(1, 1, n);
    scanf("%d", &m);
    for (int i = 0; i < m; ++i) {
        int x, l, r;
        scanf("%d", &x);
        if (x == 0) {
            scanf("%d%d", &l, &r);
            printf("%d\n", query(1, l, r));
        }
        else {
            int val;
            scanf("%d%d%d", &l, &r, &val);
            modify(1, l, r, val);
        }
    }
    return 0;
}

poj 2777

题目链接

题意:
两种操作:
1. 将一段区间内的线段都涂成给定的颜色
2. 询问一段区间内共有多少种颜色

这道题有意思的地方在于,不需要额外用一个 tag 来做标记,而可以直接用这段区间本身的属性 color 来记录:
color == -1 表示这一段的颜色不统一;
color != -1 表示这一段的颜色统一,这就充当了 tag 的作用,向下更新或者向下查询,到此即可停止;若区间更小,则需要将这个信息 push 下去

AC代码如下:

#include <cstdio>
#include <cstring>
#define lson ((rt) << 1)
#define rson ((rt) << 1 | 1)
#define maxn 100010
int ans[50];
inline int midi(int l, int r) { return (l + r) >> 1; }
struct node {
    int l, r, color;
}tree[maxn * 4];
void build(int rt, int l, int r) {
    tree[rt].l = l; tree[rt].r = r; if (rt != 1) tree[rt].color = 0;
    if (l == r) return;
    int mid = midi(l, r);
    build(lson, l, mid); build(rson, mid + 1, r);
}
void lift_up(int rt) {
    if (tree[lson].color == tree[rson].color) tree[rt].color = tree[lson].color;
}
void push_down(int rt) {
    if (tree[rt].color) {
        tree[lson].color = tree[rson].color = tree[rt].color;
        tree[rt].color = 0;
    }
}
void modify(int rt, int l, int r, int c) {
    if (tree[rt].l == l && tree[rt].r == r) {
        tree[rt].color = c;
        return;
    }
    push_down(rt);
    int mid = midi(tree[rt].l, tree[rt].r);
    if (r <= mid) modify(lson, l, r, c);
    else if (l > mid) modify(rson, l, r, c);
    else { modify(lson, l, mid, c); modify(rson, mid + 1, r, c); }
    lift_up(rt);
}
void query(int rt, int l, int r) {
//    printf("%d %d\n", rt, tree[rt].color);
    if (tree[rt].color) {
        ans[tree[rt].color] = true;
        return;
    }
    int mid = midi(tree[rt].l, tree[rt].r);
    if (r <= mid) query(lson, l, r);
    else if (l > mid) query(rson, l, r);
    else { query(lson, l, mid); query(rson, mid + 1, r); }
}
int main() {
    int n, t, o;
    scanf("%d%d%d", &n, &t, &o);
    tree[1].color = 1;
    build(1, 1, n);
    for (int i = 0; i < o; ++i) {
        char ch;
        scanf("\n%c", &ch);
        int l, r;
        if (ch == 'C') {
            int c;
            scanf("%d%d%d", &l, &r, &c);
            modify(1, l, r, c);
        }
        else {
            memset(ans, 0, sizeof(ans));
            scanf("%d%d", &l, &r);
            query(1, l, r);
            int tot = 0;
            for (int k = 1; k <= t; ++k) if (ans[k]) ++tot;
            printf("%d\n", tot);
        }
    }
    return 0;
}

hdu 4902

题目链接

题意:
两种操作:
1. 将一段区间内的数变为给定的值 x
2. 将一段区间内大于 x 的 ai 变为 gcd(x, ai)
最后输出序列

一开始想通过 tag 的叠加性来记录操作1和操作2产生的效果,
因为操作1能够抵消之前所有操作的影响,操作2(…我也不知道我当时怎么想的)

后来就乖乖地常规地写了,类似于之前涂色的那道题,每一段有个 val 属性
val != -1 表示这一段的值不统一,
val == -1 表示这一段有统一的值,可以统一进行 gcd 操作

AC代码如下:

#include <cstdio>
#define lson ((rt) << 1)
#define rson ((rt) << 1 | 1)
#define maxn 100010
inline midi(int l, int r) { return (l + r) >> 1; }
int n;
struct node {
    int l, r, val, tag;
}tree[maxn * 4];
int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}
void lift_up(int rt) {
    if (tree[lson].val == tree[rson].val) tree[rt].val = tree[lson].val;
}
void build(int rt, int l, int r) {
    tree[rt].l = l; tree[rt].r = r; tree[rt].val = -1;
    if (l == r) {
        scanf("%d", &tree[rt].val);
        return;
    }
    int mid = midi(l, r);
    build(lson, l, mid); build(rson, mid + 1, r);
    lift_up(rt);
}
void push_down(int rt) {
    if (tree[rt].val != -1) {
        tree[lson].val = tree[rson].val = tree[rt].val;
        tree[rt].val = -1;
    }
}
void modify1(int rt, int l, int r, int x) {
    if (tree[rt].l == l && tree[rt].r == r) {
        tree[rt].val = x;
        return;
    }
    push_down(rt);
    int mid = midi(tree[rt].l, tree[rt].r);
    if (r <= mid) modify1(lson, l, r, x);
    else if (l > mid) modify1(rson, l, r, x);
    else { modify1(lson, l, mid, x); modify1(rson, mid + 1, r, x); }
    lift_up(rt);
}
void modify2(int rt, int l, int r, int x) {
//    printf("%d %d %d\n", rt, l, r);
    if (tree[rt].l == l && tree[rt].r == r && tree[rt].val > -1) {
        if (tree[rt].val > x) tree[rt].val = gcd(tree[rt].val, x);
        return;
    }
    push_down(rt);
    int mid = midi(tree[rt].l, tree[rt].r);
    if (r <= mid) modify2(lson, l, r, x);
    else if (l > mid) modify2(rson, l, r, x);
    else { modify2(lson, l, mid, x); modify2(rson, mid + 1, r, x); }
    lift_up(rt);
}
void print(int rt, int l, int r) {
//    printf("%d %d %d\n", rt, l, r);
    if (tree[rt].val > -1) {
        for (int i = l; i <= r; ++i) printf("%d ", tree[rt].val);
        return;
    }
    int mid = midi(tree[rt].l, tree[rt].r);
    print(lson, l, mid); print(rson, mid + 1, r);
}
void work() {
    build(1, 1, n);
    int m;
    scanf("%d", &m);
    for (int i = 0; i < m; ++i) {
        int t, l, r, x;
        scanf("%d%d%d%d", &t, &l, &r, &x);
        if (t == 1) modify1(1, l, r, x);
        else modify2(1, l, r, x);
    }

    print(1, 1, n);
    printf("\n");
}
int main() {
//    freopen("4902.in", "r", stdin);
//    freopen("4902.out", "w", stdout);
    int T;
    scanf("%d", &T);
    while (scanf("%d", &n) != EOF) work();
    return 0;
}

进阶

hdu 3911

hdu 3397

具体见 http://blog.youkuaiyun.com/kkkkahlua/article/details/75125877

未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值