HDU4902(线段树)练习题

    如果你还是迷迷糊糊的话,建议先休息一下,前方高能!!!

    题目链接:点这里

    大概意思就是。

    给出一个数列,m次操作,序列长度和操作次数都不超过100000。
    输入:k l r x
    k表示操作类别
    操作分两种:
    (1)把[l,r]区间的数字都变成x。
    (2)把[l,r]区间中比x大的数字变成和x的最大公约数。
    输出
    操作后的数组

    如果你没学过线段树,你可能会用普通的解法,结果是超时(我试过),那么用线段树如何解呢?

    先发着,以后再写吧,不早了。zzz

    /*
    hdu4902
    线段树,书上版本
    妙妙妙呀
    */
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    #define mid (r+l>>1)
    #define lc (d<<1)
    #define rc (d<<1|1)
    const int mmax = 100002;
    
    struct tree
    {
        int lzx;//lazy懒x表示可替代x    //感觉可以用bool来替代
        int v;  //结点值,可替代x
    }tr[mmax << 2];
    
    struct queue
    {
        int k, l, r, x;
    }que[mmax];
    
    int val[mmax];
    
    int gcd(int a, int b)
    {
        return b == 0 ? a : gcd(b, a%b);
    }
    
    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].lzx = -1;
            return;
        }
        tr[d].lzx = -1;//放上面和build下面一样
        build(lc, l, mid);
        build(rc, mid + 1, r);
        push(d);
    }
    
    void lazy(int d)//懒操作de延伸
    {
        if (tr[d].lzx != -1)
        {
            tr[lc].lzx = tr[rc].lzx = tr[d].lzx;
            tr[lc].v = tr[rc].v = tr[d].v;
            tr[d].lzx = -1;//生效完还原
        }
    }
    
    void f1(int d, int l, int r, int left, int right, int x)
    {
        if (l == left && r == right)
        {
            tr[d].lzx = x;
            tr[d].v = x;
            return;
        }
        lazy(d);
        if (left > mid)  f1(rc, mid + 1, r, left, right, x);//区间在右边
        else if (right <= mid) f1(lc, l, mid, left, right, x); //等于只能右边加
        else
        {
            f1(rc, mid + 1, r, mid + 1, right, x);//这里要变一下
            f1(lc, l, mid, left, mid, x);
        }
        push(d);
    }
    
    void f2(int d, int l, int r, int left, int right, int x)
    {
        if (l == left && r == right)
        {
            if (tr[d].v <= x) return;
            if (tr[d].lzx != -1)
            {
                tr[d].v = gcd(tr[d].v, x);
                tr[d].lzx = tr[d].v;
                if (l == r) val[l] = tr[d].lzx;//感觉多余
                return;
            }
            if (l == r)//和模板很像
            {
                if (val[l] > x) val[l] = gcd(val[l], x);
                return;
            }
            lazy(d);
            f2(lc, l, mid, left, mid, x);
            f2(rc, mid + 1, r, mid + 1, right, x);
            push(d);
            return;
        }
        lazy(d);
        if (left > mid)  f2(rc, mid + 1, r, left, right, x);//区间在右边
        else if (right <= mid) f2(lc, l, mid, left, right, x); //等于可以加
        else
        {
            f2(rc, mid + 1, r, mid + 1, right, x);
            f2(lc, l, mid, left, mid, x);
        }
        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);//一样的是d
            }
            return;
        }
        if (l == r)
        {
            printf("%d ", val[l]);
            return;
        }
        lazy(d);
        out(lc, l, mid);
        out(rc, mid + 1, r);
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while (t--)
        {
            //初始化
            memset(val, 0, sizeof(val));
    
            //输入
            int n1;
            scanf("%d", &n1);
            for (int i = 1; i <= n1; i++)
            {
                scanf("%d", &val[i]);
            }
    
            build(1, 1, n1);
    
            int n2;
            scanf("%d", &n2);
            for (int i = 1; i <= n2; i++)
            {
                scanf("%d%d%d%d", &que[i].k, &que[i].l, &que[i].r, &que[i].x);
            }
    
            //处理
            for (int i = 1; i <= n2; i++)
            {
                if (que[i].k == 1)
                {
                    f1(1, 1, n1, que[i].l, que[i].r, que[i].x);
                }
                else
                {
                    f2(1, 1, n1, que[i].l, que[i].r, que[i].x);
                }
            /*    out(1, 1, n1);
                printf("\n");*/
            }
    
            out(1, 1, n1);
            printf("\n");
        }
        return 0;
    }

    下面是我的代码

    /*
    hdu4902
    线段树,自己打的
    妙妙妙呀
    */
    
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    
    #define lc (d<<1)
    #define rc (d<<1|1)
    #define mid (l+r>>1)
    
    const int mmax = 100005;
    int val[mmax];
    struct tree
    {
        int v;
        bool lz;//懒操作标记,lazy简写
    }tr[mmax << 2];
    
    int gcd(int a, int b)
    {
        return b == 0 ? a : gcd(b, a%b);
    }
    
    //懒操作
    void lazy(int d)
    {
        if (tr[d].lz)
        {
            tr[lc].v = tr[rc].v = tr[d].v;
            tr[lc].lz = tr[rc].lz = true;
            tr[d].lz = false;
        }
    }
    
    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 = false;
            return;
        }
        tr[d].lz = false;
        build(lc, l, mid);
        build(rc, mid + 1, r);
        push(d);
    }
    
    void f1(int d, int l, int r, int left, int right, int x)
    {
        if (l == left && r == right)
        {
            tr[d].lz = true;
            tr[d].v = x;
            return;
        }
        lazy(d);
        if (left > mid) f1(rc, mid+1, r, left, right, x);
        else if (right <= mid) f1(lc, l, mid, left, right, x);
        else
        {
            f1(rc, mid+1, r, mid+1, right, x);
            f1(lc, l, mid, left, mid, x);
        }
        push(d);
    }
    
    void f2(int d, int l, int r, int left, int right, int x)
    {
        if (l == left && r == right)
        {
            if (tr[d].v <= x) return;
            //lazy说明子结点的tr[d].v是一样的,于是处理父结点就可以了
            if (tr[d].lz)
            {
                tr[d].v = gcd(tr[d].v, x);
                return;
            }
            if (l == r)//没有优化的话,只能一个一个点来处理
            {
                tr[d].v = gcd(tr[d].v, x);
                return;
            }
            lazy(d);
            f2(rc, mid+1, r, mid+1, right, x);
            f2(lc, l, mid, left, mid, x);
            push(d);
            return;
        }
        lazy(d);
        if (left > mid) f2(rc, mid+1, r, left, right, x);
        else if (right <= mid) f2(lc, l, mid, left, right, x);
        else
        {
            f2(rc, mid+1, r, mid+1, right, x);
            f2(lc, l, mid, left, mid, x);
        }
        push(d);
    }
    
    void out(int d, int l, int r)
    {
        if (tr[d].lz)
        {
            for (int i = l; i <= r; i++)
            {
                printf("%d ", tr[d].v);
            }
            return;
        }
        if (l == r)
        {
            printf("%d ", tr[d].v);
            return;
        }
        //    lazy(d);
        out(lc, l, mid);
        out(rc, mid + 1, r);
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while (t--)
        {
            int n1;
            scanf("%d", &n1);
            for (int i = 1; i <= n1; i++)
            {
                scanf("%d", &val[i]);
            }
    
            build(1, 1, n1);
    
            int n2;
            scanf("%d", &n2);
            for (int i = 1; i <= n2; i++)
            {
                int k, l, r, x;
                scanf("%d%d%d%d", &k, &l, &r, &x);
                if (k == 1)
                {
                    f1(1, 1, n1, l, r, x);
                }
                else
                {
                    f2(1, 1, n1, l, r, x);
                }
            }
    
            out(1, 1, n1);
            printf("\n");
        }
        return 0;
    }
    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值