HDU4902(线段树)练习题

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

题目链接:点这里

大概意思就是。

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

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

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

/*
hdu4902
线段树,书上版本
妙妙妙呀
*/

#include
#include
#include
#include

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
#include
#include

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;
}

你可能感兴趣的:(#,------线段树(基础篇))