牛客小白月赛16 H-小阳的贝壳(区间gcd,差分数组,线段树)

该博客解析了牛客小白月赛16的题目,主要涉及区间最大颜色差值和区间最大公约数(GCD)的求解。通过使用差分数组和线段树,博主详细解释了如何处理区间GCD的计算,以及如何进行区间修改和查询。文章提供了问题的解决方案,并探讨了gcd的性质及其在区间求解中的应用。
摘要由CSDN通过智能技术生成

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

题目描述

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

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

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

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

输入描述:

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

输出描述:

共 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 ≤ 105, 1 ≤ coli ,x ≤ 103,1 ≤ opt ≤3 , 1 ≤ l ≤ r ≤ n



分析:

求区间GCD,区间修改就没法像区间求和那样用懒标记了,这里就要用到差分数组。

gcd有性质:gcd(a, b) = gcd(a, b-a)
推广一下:gcd(a, b, c) = gcd(a, b-a, c-b) = gcd(a, gcd (b-a, c-b) )

那么结合差分数组,这区间 [L, R]的gcd为:
g c d ( a [ L ] , R g c d i = L + 1 ∣ f [ i ] ∣ ) gcd(a[L],\begin{matrix} \scriptsize R\\ gcd \\ \scriptsize i=L+1 \end{matrix}|f[i]|) gcd(a[L],Rgcdi=L+1f[i])
由差分数组性质可知:
a [ x ] = ∑ i = 1 x f [ i ] a[x]=\sum_{i=1}^{x}f[i] a[x]=i=1xf[i]
所以,最终公式为:
g c d ( ∑ i = 1 L f [ i ] , R g c d i = L + 1 ∣ f [ i ] ∣ ) gcd(\sum_{i=1}^{L}f[i],\begin{matrix} \scriptsize R\\ gcd \\ \scriptsize i=L+1 \end{matrix}|f[i]|) gcd(i=1Lf[i],Rgcdi=L+1f[i])

对于求 颜色值的差(取绝对值) 的最大值,直接就是差分数组取绝对值的最大值,即
R m a x i = L + 1 ∣ f [ i ] ∣ \begin{matrix} \scriptsize R\\ max \\ \scriptsize i=L+1 \end{matrix}|f[i]| Rmaxi=L+1f[i]

对于差分数组的区间修改只需要修改 f[L] 和 f[R+1] 即可

对于操作2 的 L+1 ~ R 的最值操作3需要的1 ~ L的求和、L+1 ~ R的gcd,都可以用线段树来维护。



以下代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const int maxn=1e5+50;
int n,m,col[maxn],f[maxn];
struct node
{
    int sum;
    int gcd;
    int maxx;
    node()
    {
        sum=0;
        gcd=0;
        maxx=0;
    }
}t[4*maxn];   //线段树至少开4倍
int GCD(int a,int b)
{
    int x=a,y=b,z;
    while(y)
    {
        z=x%y;
        x=y;
        y=z;
    }
    return x;
}
void built(int rt,int l,int r)  //建树
{
    if(l==r)
    {
        t[rt].sum=f[l];
        t[rt].gcd=abs(f[l]);    //gcd 和 maxx均取绝对值
        t[rt].maxx=abs(f[l]);
        return;
    }
    int mid=(l+r)>>1;
    built(rt<<1,l,mid);
    built(rt<<1|1,mid+1,r);
    t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
    t[rt].gcd=GCD(t[rt<<1].gcd,t[rt<<1|1].gcd);
    t[rt].maxx=max(t[rt<<1].maxx,t[rt<<1|1].maxx);
}
void updata(int rt,int l,int r,int a,int b)  //单点更新
{
    if(l==r)
    {
        t[rt].sum+=b;      //注意此处的修改
        t[rt].gcd=abs(t[rt].sum);
        t[rt].maxx=abs(t[rt].sum);
        return;
    }
    int mid=(l+r)>>1;
    if(a<=mid)
        updata(rt<<1,l,mid,a,b);
    else
        updata(rt<<1|1,mid+1,r,a,b);
    t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
    t[rt].gcd=GCD(t[rt<<1].gcd,t[rt<<1|1].gcd);
    t[rt].maxx=max(t[rt<<1].maxx,t[rt<<1|1].maxx);
}
int query_sum(int rt,int l,int r,int ql,int qr)  //区间查询 sum
{
    if(ql<=l&&r<=qr)
		return t[rt].sum;
	if(r<ql||l>qr)
		return 0;
	int mid=(l+r)>>1;
	return query_sum(rt<<1,l,mid,ql,qr)+query_sum(rt<<1|1,mid+1,r,ql,qr);
}
int query_gcd(int rt,int l,int r,int ql,int qr)  //区间查询 gcd
{
    if(ql<=l&&r<=qr)
		return t[rt].gcd;
	if(r<ql||l>qr)
		return 0;   //任何数与0的gcd是其自身,所以区域不重合返回0
	int mid=(l+r)>>1;
	return GCD(query_gcd(rt<<1,l,mid,ql,qr),query_gcd(rt<<1|1,mid+1,r,ql,qr));
}
int query_maxx(int rt,int l,int r,int ql,int qr)  //区间查询 maxx
{
    if(ql<=l&&r<=qr)
		return t[rt].maxx;
	if(r<ql||l>qr)
		return 0;
	int mid=(l+r)>>1;
	return max(query_maxx(rt<<1,l,mid,ql,qr),query_maxx(rt<<1|1,mid+1,r,ql,qr));
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&col[i]);
    f[1]=col[1];
    for(int i=2;i<=n;i++)
        f[i]=col[i]-col[i-1];
    built(1,1,n);
    int opt,L,R,X;
    while(m--)
    {
        int opt;
        scanf("%d %d %d",&opt,&L,&R);
        if(opt==1)
        {
            scanf("%d",&X);
            updata(1,1,n,L,X);
            if(R+1<=n)
                updata(1,1,n,R+1,-X);
        }
        else if(opt==2)
        {
            if(L==R)
                printf("0\n");  //特判
            else
                printf("%d\n",abs(query_maxx(1,1,n,L+1,R)));
        }
        else
        {
            if(L==R)
                printf("%d\n",query_sum(1,1,n,1,L));  //特判
            else
                printf("%d\n",GCD(query_sum(1,1,n,1,L),query_gcd(1,1,n,L+1,R)));
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值