HDU 4902 Nice boat --线段树(区间更新)

题意:给一个数字序列,第一类操作是将[l,r]内的数全赋为x ,第二类操作是将[l,r]中大于x的数赋为该数与x的gcd,若干操作后输出整个序列。

解法: 本题线段树要维护的最重要的东西就是一个区间内所有数是否相等的标记。只维护这个东西都可以做出来。 我当时想歪了,想到维护Max[rt]表示该段的最大值,最大值如果<=x的话就不用更新了,但是好像加了这个“优化”跑的更慢了。

我想大概是因为如果两个子树最大值或者整个两个子树的数不完全相等的话,根本不能直接下传这个值或者下传gcd,因为你不知道要更新哪个值。所以要维护一个区间是否相等的标记,而且这样最坏情况下也是更新到叶子节点,正好使我们要更新的。

只要弄懂了要维护什么,其余的就不难了。

代码1:(加一个Max标记)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100007

int Max[4*N],mark[4*N];  //mark[rt]=-1表示此段值不统一,否则mark[rt]会等于该段的统一值

int gcd(int a,int b)
{
    if(!b) return a;
    return gcd(b,a%b);
}

void pushup(int rt)
{
    Max[rt] = max(Max[2*rt],Max[2*rt+1]);
}

void pushdown(int l,int r,int rt)
{
    if(mark[rt] != -1)  //此段值相同
    {
        mark[2*rt] = mark[2*rt+1] = mark[rt];
        Max[2*rt] = Max[2*rt+1] = Max[rt];
        mark[rt] = -1;
    }
}

void build(int l,int r,int rt)
{
    mark[rt] = -1;
    if(l == r)
    {
        scanf("%d",&Max[rt]);
        mark[rt] = Max[rt];
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,2*rt);
    build(mid+1,r,2*rt+1);
    pushup(rt);
}

void update(int l,int r,int aa,int bb,int val,int tag,int rt)
{
    if(aa <= l && bb >= r)
    {
        if(tag == 1)
        {
            Max[rt] = mark[rt] = val;
            return;
        }
        else
        {
            if(mark[rt] != -1)    //此段值相等
            {
                if(Max[rt] > val)    //要更新
                    Max[rt] = mark[rt] = gcd(mark[rt],val);
                return;
            }
        }
    }
    pushdown(l,r,rt);
    int mid = (l+r)/2;
    if(aa <= mid) update(l,mid,aa,bb,val,tag,2*rt);
    if(bb > mid)  update(mid+1,r,aa,bb,val,tag,2*rt+1);
    pushup(rt);
}

void print(int l,int r,int rt)
{
    if(l == r)
    {
        printf("%d ",Max[rt]);
        return;
    }
    pushdown(l,r,rt);
    int mid = (l+r)/2;
    print(l,mid,2*rt);
    print(mid+1,r,2*rt+1);
}

int main()
{
    int n,m,i,t,tag,aa,bb,val;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        build(1,n,1);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d%d%d%d",&tag,&aa,&bb,&val);
            update(1,n,aa,bb,val,tag,1);
        }
        print(1,n,1);
        puts("");
    }
    return 0;
}

代码2:(只维护一个)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100007

int Max[4*N];

int gcd(int a,int b)
{
    if(!b) return a;
    return gcd(b,a%b);
}

void pushup(int rt)
{
    if(Max[2*rt] == Max[2*rt+1]) Max[rt] = Max[2*rt];
}

void pushdown(int l,int r,int rt)
{
    if(Max[rt] != -1)
        Max[2*rt] = Max[2*rt+1] = Max[rt], Max[rt] = -1;
}

void build(int l,int r,int rt)
{
    Max[rt] = -1;
    if(l == r)
    {
        scanf("%d",&Max[rt]);
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,2*rt);
    build(mid+1,r,2*rt+1);
    pushup(rt);
}

void update(int l,int r,int aa,int bb,int val,int tag,int rt)
{
    if(aa <= l && bb >= r)
    {
        if(tag == 1)
        {
            Max[rt] = val;
            return;
        }
        else
        {
            if(Max[rt] != -1)
            {
                if(Max[rt] > val)    //下面有比它大的,即有要更新的
                    Max[rt] = gcd(Max[rt],val);
                return;
            }
        }
    }
    pushdown(l,r,rt);
    int mid = (l+r)/2;
    if(aa <= mid) update(l,mid,aa,bb,val,tag,2*rt);
    if(bb > mid)  update(mid+1,r,aa,bb,val,tag,2*rt+1);
    pushup(rt);
}

void print(int l,int r,int rt)
{
    if(l == r)
    {
        printf("%d ",Max[rt]);
        return;
    }
    pushdown(l,r,rt);
    int mid = (l+r)/2;
    print(l,mid,2*rt);
    print(mid+1,r,2*rt+1);
}

int main()
{
    int n,m,i,t,tag,aa,bb,val;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        build(1,n,1);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d%d%d%d",&tag,&aa,&bb,&val);
            update(1,n,aa,bb,val,tag,1);
        }
        print(1,n,1);
        puts("");
    }
    return 0;
}

时间: 2024-09-30 12:49:10

HDU 4902 Nice boat --线段树(区间更新)的相关文章

HDU 4902 Nice boat(线段树)

HDU Nice boat 题目链接 题意:给定一个序列,两种操作,把一段变成x,把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 思路:线段树,每个结点多一个cover表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候单点查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1

多校第4场 HDU 4902 Nice boat 线段树

思路:这题比赛的时候宝哥说的思路我觉得对的,就是当是2操作的时候,先把数放到数组里,最后查询输出的时候再统一计算,不过那时敲得烂死了,debug了两天,靠-- 上午写的vector在pushDown的时候又忘了clear了,然后MLE了一早上,尼玛,还以为用的数组太大超了,然后又改成结构体,还是MLE,最后把别人的代码交上去发现没MLE,疯了一中午,最后无聊的时候才发现这个错误,尼玛--发现自己调试怎么变得这么弱了呢-- 还有一个需要注意的问题是1与2操作的处理上比较容易出错,这也是我WA了一下

hdu4902Nice boat线段树区间更新

#include<iostream> #include<cmath> using namespace std; int n; bool flag; struct node { int mx,l,r,m,mn; }tree[400000]; void create(int l,int r,int k) { tree[k].l=l; tree[k].r=r; tree[k].m=(l+r)>>1; if(l==r) { cin>>tree[k].mx; tree

HDU 3577 Fast Arrangement (线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3577 题意不好理解,给你数字k表示这里车最多同时坐k个人,然后有q个询问,每个询问是每个人的上车和下车时间,每个人按次序上车,问哪些人能上车输出他们的序号. 这题用线段树的成段更新,把每个人的上下车时间看做一个线段,每次上车就把这个区间都加1,但是上车的前提是这个区间上的最大值不超过k.有个坑点就是一个人上下车的时间是左闭右开区间,可以想到要是一个人下车,另一个人上车,这个情况下这个点的大小还是不变

hdu 1698+poj 3468 (线段树 区间更新)

http://acm.hdu.edu.cn/showproblem.php?pid=1698 这个题意翻译起来有点猥琐啊,还是和谐一点吧 和涂颜色差不多,区间初始都为1,然后操作都是将x到y改为z,注意 是改为z,不是加或减,最后输出区间总值 也是线段树加lazy操作 1 #include<cstdio> 2 using namespace std; 3 struct point { 4 int l,r; 5 int val,sum; 6 }; 7 point tree[400007]; 8

hdu 3397 Sequence operation 线段树 区间更新 区间合并

题意: 5种操作,所有数字都为0或1 0 a b:将[a,b]置0 1 a b:将[a,b]置1 2 a b:[a,b]中的0和1互换 3 a b:查询[a,b]中的1的数量 4 a b:查询[a,b]中的最长连续1串的长度 这题看题目就很裸,综合了区间更新,区间合并 我一开始把更新操作全放一个变量,但是在push_down的时候很麻烦,情况很多,容易漏,后来改成下面的 更新的操作可以分为两类,一个是置值(stv),一个是互换(swp).如果stv!=-1,则更新儿子节点的stv,并将儿子的sw

HDU 4902 Nice boat 线段树+离线

据说暴力也过了.还傻逼地写了这么长. . . #include <stdio.h> #include <string.h> #include <math.h> #include <iostream> using namespace std; #define ll long long #define L(x) (x<<1) #define R(x) (x<<1|1) #define Val(x) tree[x].val #define

HDU 4902 Nice boat(线段树 区间更新)

Nice boat 大意:给你一个区间,每次可以进行两种操作,1:把区间中的数全都变成x  2:把区间中大于x的数变成gcd(a[i], x),最后输出序列. 思路:线段树成段更行,用num数组的叶子存储数据,节点当作lazy来使用. 1 #include <stdio.h> 2 const int maxn = 100005; 3 4 int num[maxn<<2]; 5 6 int gcd(int a, int b){ 7 return b?gcd(b, a%b):a; 8

HDU 4902 Nice boat 2014杭电多校训练赛第四场F题(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 解题报告:输入一个序列,然后有q次操作,操作有两种,第一种是把区间 (l,r) 变成x,第二种是把区间 (l,r) 中大于x的数跟 x 做gcd操作. 线段树区间更新的题目,每个节点保存一个最大和最小值,当该节点的最大值和最小值相等的时候表示这个区间所有的数字都是相同的,可以直接对这个区间进行1或2操作, 进行1操作时,当还没有到达要操作的区间但已经出现了节点的最大值跟最小值相等的情况时,说明