线段树 lazy tag 小合集

来源:互联网 发布:彩虹六号枪械数据 编辑:程序博客网 时间:2024/06/09 19:35

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

  • 基础
    • poj 3468
    • hiho 1078
    • poj 2777
    • hdu 4902
  • 进阶
    • hdu 3911
    • hdu 3397
  • 未完待续

基础

poj 3468

题目链接

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

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

AC代码如下:

#include <cstdio>#define MAX 100010typedef 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 100010int 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 100010inline 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.csdn.net/kkkkahlua/article/details/75125877

未完待续

阅读全文
0 0
原创粉丝点击