NC26255小阳的贝壳(线段树+区间维护gcd+差分数组)

链接:https://ac.nowcoder.com/acm/problem/26255
来源:牛客网

题目描述

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

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

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

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

输入描述:

第一行输入两个正整数 n,mn,mn,m,分别表示贝壳个数和操作个数。
第二行输入 nnn 个数 colicol_icoli,表示每个贝壳的初始颜色。
第三到第 m+2m + 2m+2 行,每行第一个数为 optoptopt,表示操作编号。接下来的输入的变量与操作编号对应。

输出描述:

共 m 行,对于每个询问(操作 2 和操作 3)输出对应的结果。

思路

题目是关于区间修改和区间查询的,考虑线段树或者树状数组,舍去树状数组。

如何用线段树去写?如果只是维护原数组的话肯定是没办法解决问题的。

所以用线段树去维护差分数组 d [ ] d[] d[]

对于操作1,对应于差分数组的 d [ l ] + x , d [ r + 1 ] − x d[l]+x,d[r+1]-x d[l]+x,d[r+1]x操作。因此考虑用线段树进行单点修改。

对于操作2,求的是区间 [ l , r ] [l,r] [l,r] ∣ t i − t i − 1 ∣ ( l + 1 ) ≤ i ≤ j |t_i-t_{i-1}|(l+1)\leq i\leq j titi1(l+1)ij的最大值,也就是差分数组的 d [ i ] d[i] d[i]的最大值。因子考虑用线段树维护 ∣ d [ i ] ∣ |d[i]| d[i]的最大值

对于操作3,
g c d ( t l , t l + 1 , t l + 2 , … , t r ) = g c d ( t l , ∣ t l + 1 − t l ∣ , … , ∣ t r − t r − 1 ∣ ) gcd(t_l,t_{l+1},t_{l+2},…,t_{r})=gcd(t_l,|t_{l+1}-t_l|,…,|t_{r}-t_{r-1}|) gcd(tl,tl+1,tl+2,,tr)=gcd(tl,tl+1tl,,trtr1)

= g c d ( t l , ∣ d l + 1 ∣ , ∣ d l + 2 ∣ , … , ∣ d r ∣ ) =gcd(t_l,|d_{l+1}|,|d_{l+2}|,…,|d_r|) =gcd(tl,dl+1,dl+2,,dr)

= g c d ( ∑ i = 1 l d i , ∣ d l + 1 ∣ , … , ∣ d r ∣ ) =gcd(\sum_{i=1}^{l}d_i,|d_{l+1}|,…,|d_r|) =gcd(i=1ldi,dl+1,,dr)

因此考虑用线段树维护区间内 ∣ d i ∣ |d_i| di的最小公倍数和其区间和。

综上所述,线段树节点包含的信息有,区间内 d i d_i di的和、区间内 ∣ d i ∣ |d_i| di的最大公约数、区间内 d i d_i di的值。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define DOF 0x7f7f7f7f
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define debug(case,x); cout<<case<<"  : "<<x<<endl;
#define open freopen("ii.txt","r",stdin)
#define close freopen("oo.txt","w",stdout)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pb push_back
using namespace std;
#define int long long
#define lson rt<<1
#define rson rt<<1|1
const int maxn=1e5+10;

int t[maxn],d[maxn];

struct tree{
    int l,r;
    int sum,maxx,gcd;
}tree[maxn<<2];

void push_up(int rt){
    tree[rt].sum=tree[lson].sum+tree[rson].sum;
    tree[rt].gcd=__gcd(tree[lson].gcd,tree[rson].gcd);
    tree[rt].maxx=max(tree[lson].maxx,tree[rson].maxx);
}


void build(int rt,int l,int r){
    tree[rt].l=l,tree[rt].r=r;
    if(l==r){
        tree[rt].maxx=tree[rt].gcd=abs(d[l]);
        tree[rt].sum=d[l];
        return ;
    }

    int mid=(l+r)>>1;

    build(lson,l,mid);
    build(rson,mid+1,r);
    push_up(rt);

}

void update_point(int rt,int x,int k){
    if(tree[rt].l==x&&tree[rt].r==x){
        tree[rt].sum+=k;
        tree[rt].maxx=tree[rt].gcd=abs(tree[rt].sum);
        return ;
    }

    int mid=(tree[rt].r+tree[rt].l)>>1;

    if(x<=mid)update_point(lson,x,k);
    else update_point(rson,x,k);

    push_up(rt);

}

int query_sum(int rt,int l,int r){
    if(tree[rt].l>=l&&tree[rt].r<=r){
        return tree[rt].sum;
    }
    

    int mid=(tree[rt].l+tree[rt].r)>>1;
    int res=0;

    if(l<=mid){
        res+=query_sum(lson,l,r);
    }
    if(r>mid){
        res+=query_sum(rson,l,r);
    }
    return res;

}


int query_gcd(int rt,int l,int r){
    if(l<=tree[rt].l&&tree[rt].r<=r)
        return tree[rt].gcd;

    int mid=(tree[rt].l+tree[rt].r)>>1;
    int res=0;

    if(l<=mid) res=__gcd(res,query_gcd(lson,l,r));
    if(r>mid) res=__gcd(res,query_gcd(rson,l,r));
    return res;

}

int query_max(int rt,int l,int r){
    if(tree[rt].l>=l&&tree[rt].r<=r)
        return tree[rt].maxx;
    int mid=(tree[rt].l+tree[rt].r)>>1;
    int res=0;

    if(l<=mid) res=max(res,query_max(lson,l,r));
    if(r>mid) res=max(res,query_max(rson,l,r));

    return res;

}


signed main(){
    int n,m;scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%lld",&t[i]);
        d[i]=t[i]-t[i-1];
    }
    build(1,1,n);
    while(m--){
        int op,l,r;scanf("%lld%lld%lld",&op,&l,&r);
        if(op==1){
            int x;scanf("%lld",&x);
            update_point(1,l,x);
            if(r<n) update_point(1,r+1,-x);
        }else if(op==2){
            if(l==r)printf("0\n");
            else printf("%lld\n",query_max(1,l+1,r));
        }else{
            printf("%lld\n",__gcd(query_sum(1,1,l),query_gcd(1,l+1,r)));
        }
    }



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值