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[]\)

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

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

对于操作3,

\[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(t_l,|d_{l+1}|,|d_{l+2}|,…,|d_r|) \]

\[=gcd(\sum_{i=1}^{l}d_i,|d_{l+1}|,…,|d_r|) \]

因此考虑用线段树维护区间内\(|d_i|\)的最小公倍数和其区间和。

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

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



}
posted @ 2020-07-21 20:24  waryan  阅读(145)  评论(0)    收藏  举报