牛客小白月赛16 H 小阳的贝壳 (差分+线段树)
链接:https://ac.nowcoder.com/acm/contest/949/H
来源:牛客网
题目描述
小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 colicoli 。现在小阳有 3 种操作:
1 l r x:给 [l,r][l,r] 区间里所有贝壳的颜色值加上 xx 。
2 l r:询问 [l,r][l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=rl=r 输出 0)。
3 l r :询问 [l,r][l,r] 区间里所有贝壳颜色值的最大公约数。
1 l r x:给 [l,r][l,r] 区间里所有贝壳的颜色值加上 xx 。
2 l r:询问 [l,r][l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=rl=r 输出 0)。
3 l r :询问 [l,r][l,r] 区间里所有贝壳颜色值的最大公约数。
输入描述:
第一行输入两个正整数 n,mn,m,分别表示贝壳个数和操作个数。
第二行输入 nn 个数 colicoli,表示每个贝壳的初始颜色。
第三到第 m+2m+2 行,每行第一个数为 optopt,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述:
共 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(a,b) == gcd(a,b-a),那么我们把原数组差分一下,这样区间修改就只用修改两个点了,求相邻两点的差值也是一样的,线段树上多维护个mx[]就好了
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mid ll m = (l + r) >> 1 const ll M = 2e5+10; ll a[M],b[M],c[M],sum[M<<2],mx[M<<2],n; ll lowbit(ll x) { return x & -x; } void add(ll x,ll val){ for(ll i = x;i <= M;i += lowbit(i)){ c[i] += val; } } ll getsum(ll x){ ll ans = 0; for(ll i = x;i > 0;i -= lowbit(i)){ ans += c[i]; } return ans; } void up(ll rt){ sum[rt] = __gcd(sum[rt<<1] ,sum[rt<<1|1]); mx[rt] = max(abs(mx[rt<<1]),abs(mx[rt<<1|1])); } void build(ll l,ll r,ll rt){ if(l == r){ sum[rt] = b[l]; mx[rt] = b[l]; return ; } mid; build(lson); build(rson); up(rt); } void update(ll p,ll c,ll l,ll r,ll rt){ if(l == r){ sum[rt] += c; mx[rt] += c; return ; } mid; if(p <= m) update(p,c,lson); else update(p,c,rson); up(rt); } ll query_gcd(ll L,ll R,ll l,ll r,ll rt){ if(L <= l&&R >= r){ return sum[rt]; } mid; ll ret = 0; if(L <= m) ret = __gcd(ret,query_gcd(L,R,lson)); if(R > m) ret = __gcd(ret,query_gcd(L,R,rson)); return abs(ret); } ll query_max(ll L,ll R,ll l,ll r,ll rt){ if(L <= l&&R >= r){ return mx[rt]; } mid; ll ret = 0; if(L <= m) ret = max(ret,abs(query_max(L,R,lson))); if(R > m) ret = max(ret,abs(query_max(L,R,rson))); return ret; } int main() { ll m,l,r,x; scanf("%lld%lld",&n,&m); for(ll i = 1;i <= n;i ++) scanf("%lld",&a[i]),b[i]=a[i]-a[i-1]; build(1,n,1); ll op; for(ll i = 1;i <= m;i ++){ scanf("%lld",&op); if(op == 1){ scanf("%lld%lld%lld",&l,&r,&x); update(l,x,1,n,1); if(r < n) update(r+1,-x,1,n,1); add(l,x); add(r+1,-x); } else if(op == 2){ scanf("%lld%lld",&l,&r); ll ans; if(l!=r) ans = query_max(l+1,r,1,n,1); else ans = 0; printf("%lld\n",ans); } else { scanf("%lld%lld",&l,&r); ll cnt = a[l] + getsum(l); if(l != r){ ll ans = query_gcd(l+1,r,1,n,1); printf("%lld\n",__gcd(cnt,ans)); } else { printf("%lld\n",cnt); } } } return 0; }