• 牛客小白月赛16


    链接:https://ac.nowcoder.com/acm/contest/949/H
    来源:牛客网

    题目描述

    小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 coli i​ 。现在小阳有 3 种操作:

    1 l r x:给 [l,r] 区间里所有贝壳的颜色值加上 xxx 。

    2 l r:询问 [l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=rl = rl=r 输出 0)。

    3 l r :询问 [l,r] 区间里所有贝壳颜色值的最大公约数。

    输入描述:

    第一行输入两个正整数 n,mn,mn,m,分别表示贝壳个数和操作个数。
    第二行输入 nnn 个数 colicol_icoli​,表示每个贝壳的初始颜色。
    第三到第 m+2m + 2m+2 行,每行第一个数为 optoptopt,表示操作编号。接下来的输入的变量与操作编号对应。
    输出描述:
    共 m 行,对于每个询问(操作 2 和操作 3)输出对应的结果。
    示例1
    输入
    5 6
    2 2 3 3 3
    1 2 3 3
    2 2 4
    3 3 5
    1 1 4 2
    3 2 3
    2 3 5
    输出
    3
    3
    1
    3
    备注:
    1≤n,m≤100000

    1≤coli≤1000

    1≤opt≤3,1≤l≤r≤n

    题目大意:

    线段树操作集合问题,我们发现有三种操作,第一种 区间更新,l-r 区间每个数的值 +x 第二种 相邻最大值,即区间各数之间相邻的最大值的绝对值,第三种,求区间的gcd。

    解题思路:

    第一次碰到这种问题,往常的区间更新都是借用一个lazy数组来完成,但考虑到这个题还有相邻之间的距离最大值,还有区间gcd,显然用lazy肯定不行,通过这个题我学到了差分数组的思想。
    差分数组:即数组中存放的不是原来的值,而是当前的数和前一个数的差值:
    举个例子: 原数组 1 3 5 10 8 ——> 差分数组 1 2 2 5 -2
    我们还可以发现一个规律,对差分数组求前缀和就是原来的第n项。
    不难发现,差分数组的值就是相邻之间的差值,所以可以用线段树维护差分数组的绝对值的最大值。
    关于区间更新:在差分数组中可以这样实现,举个例子:【3,5】这个区间内的每一个数都+2,因为差分数组中存放的是一个差值,所以我们在3这个位置+2,在6这个位置-2 就可以实现【3,5】每个数都+2了(可以自己带一组数据试试)。
    再谈gcd问题 gcd(a,b)= gcd(a,b-a)同样可以用差分数组来实现,所以其他的不变,只需要把原来建树的普通数组改成差分数组即可,其他操作都用线段树来实现。AC代码:

    #include <iostream>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    const int _max = 1e5+50;
    int a[_max];
    struct node//线段树中存放的值
    {
        int sum,mx,gd;
    }tree[_max<<2];
    int gcd(int a,int b)//欧几里得求gcd
    {
        return b==0?a:gcd(b,a%b);
    }
    void pushup(int k)//线段树常规更新函数
    {
        tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
        tree[k].gd=gcd(tree[k<<1].gd,tree[k<<1|1].gd);
        tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
    }
    void build(int node,int l,int r)
    {
        if(l==r)
        {
            tree[node].mx=abs(a[l]);//因为要求的是绝对值,所以这里维护一下绝对值。
            tree[node].gd=abs(a[l]);//gcd同样存一下绝对值
            tree[node].sum=a[l];
            return;
        }
        int mid=(l+r)>>1;
        build(node<<1,l,mid);
        build(node<<1|1,mid+1,r);
        pushup(node);
    }
    void update(int node,int l,int r,int idx,int val)//区间更新
    {
        if(l==r)
        {
            tree[node].sum+=val;
            tree[node].mx=tree[node].gd=abs(tree[node].sum);
            return;
        }
        int mid=(l+r)>>1;
        if(idx<=mid)
          update(node<<1,l,mid,idx,val);
        else
          update(node<<1|1,mid+1,r,idx,val);
        pushup(node);
    }
    int query_sum(int node,int l,int r,int start,int end)//区间求和,维护一下L==R的情况
    {
        if(l>=start&&r<=end)
          return tree[node].sum;
        int mid=(l+r)>>1;
        int ans=0;
        if(start<=mid)
          ans+=query_sum(node<<1,l,mid,start,end);
        if(end>mid)
          ans+=query_sum(node<<1|1,mid+1,r,start,end);
        return ans;  
    }
    int query_max(int node,int l,int r,int start,int end)//区间最大值
    {
        if(l>=start&&r<=end)
          return tree[node].mx;
        int mid=(l+r)>>1;
        int ans=-1;
        if(start<=mid)
          ans=max(query_max(node<<1,l,mid,start,end),ans);
        if(end>mid)
          ans=max(query_max(node<<1|1,mid+1,r,start,end),ans);
        return ans;
    }
    int query_gcd(int node,int l,int r,int start,int end)//区间gcd
    {
        if(l>=start&&r<=end)
          return tree[node].gd;
        int mid=(l+r)>>1;
        int ans=0;
        if(start<=mid)
          ans=gcd(query_gcd(node<<1,l,mid,start,end),ans);
        if(end>mid)
          ans=gcd(query_gcd(node<<1|1,mid+1,r,start,end),ans);
        return ans;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
          cin>>a[i];
        a[0]=0;
        for(int i=n;i>=1;i--)
          a[i]-=a[i-1];
        build(1,1,n);
        while(m--)
        {
            int op,l,r;
            cin>>op>>l>>r;
            if(op==1)
            {
                int x;
                cin>>x;
                update(1,1,n,l,x);//l  +x    r+1  -x
                if(r<n)
                  update(1,1,n,r+1,-x);
            }
            else if(op==2)
            {
                if(l==r)
                  cout<<0<<endl;
                else
                {
                    int ans=query_max(1,1,n,l+1,r);
                    cout<<ans<<endl;
                }
            }
            else
            {
                int ans=abs(query_sum(1,1,n,1,l));
                if(l==r)
                   cout<<ans<<endl;
                else
                {
                    ans=gcd(ans,query_gcd(1,1,n,l+1,r));
                    cout<<ans<<endl;
                }
            }
        }
        //system("pause");
        return 0;
    }
    
  • 相关阅读:
    语言基础
    进制转换
    Java基础相关
    Java基础了解
    php 条件查询和多条件查询
    php 增删改查练习
    php 用封装类的方法操作数据库和批量删除
    php 用面向对象的方法对数据库增删改查
    php 面向对象的方式访问数据库
    OOP 7大原则
  • 原文地址:https://www.cnblogs.com/Hayasaka/p/14294265.html
Copyright © 2020-2023  润新知