hdu 4902 Nice boat 多校第四场 线段树的区间置数+区间更新

本文详细解析了一个关于线段树懒惰标记的问题,包括输入输出格式、具体实现代码及调试过程。通过实例演示了如何使用线段树解决特定类型的区间修改与查询问题。

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

Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

There are n numbers a_1,a_2,...,a_n on a line, everytime you can change every number in a segment [l,r] into a number x(type 1), or change every number a_i in a segment [l,r] which is bigger than x to gcd(a_i,x) (type 2).

You should output the final sequence.


Input
The first line contains an integer T, denoting the number of the test cases.
For each test case, the first line contains a integers n.
The next line contains n integers a_1,a_2,...,a_n separated by a single space.
The next line contains an integer Q, denoting the number of the operations.
The next Q line contains 4 integers t,l,r,x. t denotes the operation type.
T<=2,n,Q<=100000
a_i,x >=0
a_i,x is in the range of int32(C++)
 Output
For each test case, output a line with n integers separated by a single space representing the final sequence.
Please output a single more space after end of the sequence
 Sample Input
1 10 16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709 10 1 3 6 74243042 2 4 8 16531729 1 3 4 1474833169 2 1 8 1131570933 2 7 9 1505795335 2 3 7 101929267 1 4 10 1624379149 2 2 8 2110010672 2 6 7 156091745 1 2 5 937186357
Sample Output
16807 937186357 937186357 937186357 937186357 1 1 1624379149 1624379149 1624379149
线段树的懒惰标记。。调了我好长好长时间。。题目给了15秒,比赛的时候有人用纯模拟给过了,Orz。。标程给的是用面向对象的线段树写的,跑了5700ms。。用线段树只需要800ms。。好不容易调试出来的,下面写了详细的注释
//828MS	1972K
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define lch(x) ((x)<<1)
#define rch(x) ((x)<<1 | 1)
using namespace std;

const int maxn = 100000;
int num[maxn+5];
int tree[maxn<<2];
bool flag[maxn<<2];

int gcd(int x, int y){
    return y == 0 ? x : gcd(y, x%y);
}

void build(int n, int l, int r){
    flag[n] = false; //所有节点标记置0
    if(l == r){
        tree[n] = num[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(lch(n), l, mid);
    build(rch(n), mid+1, r);
}

void push_down(int n){
    if(flag[n]){ //有标记才下传
        flag[lch(n)] = flag[rch(n)] = true;
        tree[lch(n)] = tree[rch(n)] = tree[n];
        flag[n] = false;
    }
}

void seg_set(int a, int b, int v, int n, int l, int r){
    if(a <= l && r <= b){
        tree[n] = v;
        flag[n] = true;
        return;
    }
    push_down(n); //区间[l, r]没有完全被[a, b]覆盖,则需要尝试标记下传
    int mid = (l + r) >> 1;
    if(b <= mid) seg_set(a, b, v, lch(n), l, mid); //抛弃右段
    else if(mid+1 <= a) seg_set(a, b, v, rch(n), mid+1, r); //抛弃左段
    else {
        seg_set(a, b, v, lch(n), l, mid);
        seg_set(a, b, v, rch(n), mid+1, r);
    }
}

void modify(int a, int b, int x, int n, int l, int r){
    if(flag[n] && a <= l && r <= b){ //边界1:到了标记节点且该节点对应的区间被完全覆盖
       if(tree[n] > x) tree[n] = gcd(tree[n], x);
       return;
    }
    if(l == r){ //边界2:到了叶子节点
        if(tree[n] > x) tree[n] = gcd(tree[n], x);
        return;
    }
    push_down(n);
    int mid = (l + r) >> 1;
    if(b <= mid) modify(a, b, x, lch(n), l, mid); //抛弃右段
    else if(mid+1 <= a) modify(a, b, x, rch(n), mid+1, r); //抛弃左段
    else {
        modify(a, b, x, lch(n), l, mid);
        modify(a, b, x, rch(n), mid+1, r);
    }
}

void print(int a, int b, int n, int l, int r){
    if(flag[n]){ //边界1:到了标记节点
        for(int i=l; i<=r; i++) printf("%d ", tree[n]);
        return;
    }
    if(l == r){ //边界2:到了叶子节点
        printf("%d ", tree[n]);
        return;
    }
    int mid = (l+r)>>1;
    print(a, b, lch(n), l, mid);
    print(a, b, rch(n), mid+1, r);
}

int main(){
    int T; scanf("%d", &T);
    while(T--){
        int n, Q;
        scanf("%d", &n);
        for(int i=1; i<=n; i++)
            scanf("%d", &num[i]);
        build(1, 1, n);
        scanf("%d", &Q);
        while(Q--){
            int t, l, r, x;
            scanf("%d%d%d%d", &t, &l, &r, &x);
            if(t == 1) seg_set(l, r, x, 1, 1, n);
            else modify(l, r, x, 1, 1, n);
        }
        print(1, n, 1, 1, n);
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值