题目描述
小阳手中一共有 n n n 个贝壳,每个贝壳都有颜色,且初始第 i i i 个贝壳的颜色为 c o l i col_i co l i 。 现在小阳有 3 3 3 种操作。
1 l r x \text{1 l r x} 1 l r x :给 [ l , r ] [l,r] [ l , r ] 区间里所有贝壳的颜色值加上 x x x 。
2 l r \text{2 l r} 2 l r :询问[ l , r ] [l,r] [ l , r ] 区间里所有相邻贝壳颜色值的差(取绝对值) 的最大值(若 l = r l=r l = r 输出 0 0 0 )。
3 l r \text{3 l r} 3 l r :询问 [ l , r ] [l,r] [ l , r ] 区间里所有贝壳颜色值的最大公约数。
输入描述
第一行输入两个正整数 n , m n,m n , m ,分别表示贝壳个数和操作个数。
第二行输入 n n n 个数 c o l i col_i co l i ,表示每个贝壳的初始颜色。
第三到第 m + 2 m + 2 m + 2 行,每行第一个数为 o p t opt o pt ,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述
共 m m m 行,对于每个询问(操作 2 2 2 和操作 3 3 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 ≤ 1 0 5 , 1 ≤ c o l i , x ≤ 1 0 3 , 1 ≤ o p t ≤ 3 , 1 ≤ l ≤ r ≤ n 1 \leq n,m \leq 10^5,1 \leq col_i,x \leq 10^3,1 \leq opt \leq 3,1 \leq l \leq r \leq n 1 ≤ n , m ≤ 1 0 5 , 1 ≤ co l i , x ≤ 1 0 3 , 1 ≤ o pt ≤ 3 , 1 ≤ l ≤ r ≤ n 。
分析
区间修改查询的操作,第一反应是区间修改和区间查询的线段树,但是一时无从下手。
对于数组的区间修改,另一个常用的策略是使用差分数组,设数组 c o l i col_i co l i 的差分数组为 d i d_i d i 。
进行操作 1 1 1 区间加时,相当于对差分数组 d l d_l d l 和 d r + 1 d_{r+1} d r + 1 的单点修改。因此不妨用一颗线段树维护 d i d_i d i ,对其进行单点修改。
进行操作 2 2 2 时,求的是区间 [ l , r ] [l,r] [ l , r ] 内 ∣ c o l i − c o l i − 1 ∣ ( l + 1 ≤ i ≤ r ) |col_i-col_{i-1}|(l+1\le i\le r) ∣ co l i − co l i − 1 ∣ ( l + 1 ≤ i ≤ r ) 的最大值,也就是求区间[ l + 1 , r ] [l+1,r] [ l + 1 , r ] 内 ∣ d i ∣ |d_i| ∣ d i ∣ 的最大值。因此不妨用一颗线段树维护区间内 ∣ d i ∣ |d_i| ∣ d i ∣ 的最大值。
进行操作 3 3 3 时,显然有 gcd ( c o l l , c o l l + 1 , ⋯ , c o l r ) \gcd(col_l,col_{l+1},\cdots,col_r) g cd( co l l , co l l + 1 , ⋯ , co l r ) = = = gcd ( c o l l , ∣ c o l l + 1 − c o l l ∣ , ⋯ , ∣ c o l r − c o l r − 1 ∣ ) \gcd(col_l,|col_{l+1}-col_l|,\cdots,|col_r-col_{r-1}|) g cd( co l l , ∣ co l l + 1 − co l l ∣ , ⋯ , ∣ co l r − co l r − 1 ∣ ) = = = gcd ( c o l i , ∣ d l + 1 ∣ , ⋯ , ∣ d r ∣ ) \gcd(col_i,|d_{l+1}|,\cdots,|d_r|) g cd( co l i , ∣ d l + 1 ∣ , ⋯ , ∣ d r ∣ ) = = = gcd ( ∑ i = 1 l d i , ∣ d l + 1 ∣ , ⋯ , ∣ d r ∣ ) \gcd(\sum\limits_{i=1}^l d_i,|d_{l+1}|,\cdots,|d_r|) g cd( i = 1 ∑ l d i , ∣ d l + 1 ∣ , ⋯ , ∣ d r ∣ ) 。因此不妨用一颗线段树维护区间内 ∣ d i ∣ |d_i| ∣ d i ∣ 的最小公倍数和 d i d_i d i 的区间和。
综上所述,线段树上节点包含的信息有,区间内 d i d_i d i 的和、区间内∣ d i ∣ |d_i| ∣ d i ∣ 的最大公约数、区间内 ∣ d i ∣ |d_i| ∣ d i ∣ 的最大值。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #define N 100006 using namespace std;int col[N];int d[N];int n,m;struct SegmentTree { int l,r; int maximum; int gcd; int sum; }tree[N<<2 ]; void pushup (int id) { int ls=id<<1 ; int rs=id<<1 |1 ; tree[id].gcd=__gcd(tree[ls].gcd,tree[rs].gcd); tree[id].maximum=max (tree[ls].maximum,tree[rs].maximum); tree[id].sum=tree[ls].sum+tree[rs].sum; } void build (int id,int l,int r) { tree[id].l=l; tree[id].r=r; if (l==r) { tree[id].gcd=tree[id].maximum=abs (d[l]); tree[id].sum=d[l]; return ; } int mid=l+r>>1 ; build (id<<1 ,l,mid); build (id<<1 |1 ,mid+1 ,r); pushup (id); } int query_gcd (int id,int l,int r) { if (l<=tree[id].l&&tree[id].r<=r) return tree[id].gcd; int mid=tree[id].l+tree[id].r>>1 ; int res=0 ; if (l<=mid) res=__gcd(res,query_gcd (id<<1 ,l,r)); if (r>=mid+1 ) res=__gcd(res,query_gcd (id<<1 |1 ,l,r)); return res; } int query_sum (int id,int l,int r) { if (l<=tree[id].l&&tree[id].r<=r) return tree[id].sum; int mid=tree[id].l+tree[id].r>>1 ; int res=0 ; if (l<=mid) res+=query_sum (id<<1 ,l,r); if (r>=mid+1 ) res+=query_sum (id<<1 |1 ,l,r); return res; } int query_max (int id,int l,int r) { if (l<=tree[id].l&&tree[id].r<=r) return tree[id].maximum; int mid=tree[id].l+tree[id].r>>1 ; int res=0 ; if (l<=mid) res=max (res,query_max (id<<1 ,l,r)); if (r>=mid+1 ) res=max (res,query_max (id<<1 |1 ,l,r)); return res; } void update (int id,int pos,int x) { if (tree[id].l==pos&&tree[id].r==pos) { tree[id].sum+=x; tree[id].maximum=abs (tree[id].sum); tree[id].gcd=abs (tree[id].sum); return ; } int mid=tree[id].l+tree[id].r>>1 ; if (pos<=mid) update (id<<1 ,pos,x); else update (id<<1 |1 ,pos,x); pushup (id); } int main () { cin>>n>>m; int i; for (i=1 ;i<=n;i++) scanf ("%d" ,col+i); for (i=1 ;i<=n;i++) d[i]=col[i]-col[i-1 ]; build (1 ,1 ,n); while (m--) { int opt,l,r; scanf ("%d%d%d" ,&opt,&l,&r); if (opt==2 ) l==r?puts ("0" ):printf ("%d\n" ,query_max (1 ,l+1 ,r)); else if (opt==3 ) printf ("%d\n" ,__gcd(query_sum (1 ,1 ,l),query_gcd (1 ,l+1 ,r))); else { int x; scanf ("%lld" ,&x); update (1 ,l,x); if (r<n) update (1 ,r+1 ,-x); } } return 0 ; }