【HDU 4902 多校联合】Nice boat【线段树】

15 篇文章 0 订阅
7 篇文章 0 订阅

题意:给出一个数列,m次操作,分为两种操作,1.把【L,R】区间的数字都变成x,2.把【L,R】区间中比x大的数字变成和x的最大公约数。

思路:利用线段树来做,节点存的是区间最大值,以及区间的数字是否一样,update就有两种,一种是把一段区间全部变成X,这就是区间修改的事情, 另一种是看区间最大值是否是大于X,如果不大于则不用修改,否则查看区间的数字是否为一个值,是的话直接区间修改,否则继续更新孩子节点。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100005
#define mid (r+l>>1)
#define lc (d<<1)
#define rc (d<<1|1)

struct Tr
{
    int lz, v, lzx;
}tr[N<<2];

struct Q{
    int t, l, r, x;
}que[N];

int val[N];

void Push(int d) {
    tr[d].v = max(tr[lc].v, tr[rc].v);
}

void build(int d, int l, int r) {
    if (l == r) {
        tr[d].v = val[l];
        tr[d].lz = 0, tr[d].lzx = -1;
        return;
    }
    tr[d].lz = 0, tr[d].lzx = -1;
    build(lc, l,  mid);
    build(rc, mid+1, r);
    Push(d);
}

void lazy(int d) {
    if (tr[d].lzx != -1) {
        tr[lc].lzx = tr[d].lzx, tr[rc].lzx = tr[d].lzx;
        tr[lc].v = tr[d].lzx, tr[rc].v = tr[d].lzx;
        tr[d].lzx = -1;
    }
}
int cal(int a, int b) {
    if (b == 0) return a;
    return cal(b, a%b);
}

void updatex(int d, int l, int r, int L, int R, int x, int t) {
    if (l == L && r == R) {
        tr[d].lzx = x;
        tr[d].v = x;
        return;
    }
    lazy(d);
    if (R <= mid) updatex(lc, l, mid, L, R, x, t);
    else if (L > mid) updatex(rc, mid+1, r, L, R, x, t);
    else updatex(lc, l, mid, L, mid, x, t), updatex(rc, mid+1, r, mid+1, R, x, t);
    Push(d);
}

void update(int d, int l, int r, int L, int R, int x, int t) {
    if (l == L && r == R) {
        if (tr[d].v <= x) return;
        if (tr[d].lzx != -1) {
            tr[d].lzx = cal(tr[d].lzx, x);
            tr[d].v = tr[d].lzx;
            if (l == r) val[l] = tr[d].lzx;
            return;
        }
        if (l == r) {
            if (val[l] > x) val[l] = cal(val[l], x);
            return;
        }
        lazy(d);
        update(lc, l, mid, L, mid, x, t);
        update(rc, mid+1, r, mid+1, R, x, t);
        Push(d);
        return;
    }
    lazy(d);
    if (R <= mid) update(lc, l, mid, L, R, x, t);
    else if (L > mid) update(rc, mid+1, r, L, R, x, t);
    else update(lc, l, mid, L, mid, x, t), update(rc, mid+1, r, mid+1, R, x, t);
    Push(d);
}

void out(int d, int l, int r) {
    if (tr[d].lzx != -1) {
        for (int i = l;i <= r;i++) printf("%d ", tr[d].lzx);
        return;
    }
    if (l == r) {
        printf("%d ", val[l]);
        return;
    }
    lazy(d);
    out(lc, l, mid);
    out(rc, mid+1, r);
}

int main() {
    int T, n, i, j, m;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (i = 1;i <= n;i++) {
            scanf("%d", &val[i]);
        }
        build(1, 1, n);
        scanf("%d", &m);
        for (i = 1;i <= m;i++) {
            scanf("%d%d%d%d", &que[i].t, &que[i].l, &que[i].r, &que[i].x);
        }
        for (i = 1;i <= m;i++) {
            if (que[i].t == 1) {
                updatex(1, 1, n, que[i].l, que[i].r, que[i].x, i);
            }else {
                update(1, 1, n, que[i].l, que[i].r, que[i].x, i);
            }
        }
        out(1, 1, n);puts("");
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值