C 小阳的贝壳 基本线段树+gcd性质
链接:https://ac.nowcoder.com/acm/contest/949/H
这道题就是一道很基本的线段树的题目,但基本上把线段树的基础各种操作都放进去了。
我们知道,线段树维护和统计的东西要满足区间加法,而其中就包括了以下三个:
数字之和——总数字之和 = 左区间数字之和 + 右区间数字之和
最大公因数(GCD)——总GCD = gcd( 左区间GCD , 右区间GCD );
最大值——总最大值=max(左区间最大值,右区间最大值)
而这题全都包含了,所以可以当做一个比较好的板子。
以下是代码。
其中 __gcd 是c++库里的一个函数,跟手动gcd求最大公约数一样。
而求区间所有数的最大公约数的方法:
gcd(a,b)=gcd(a,b−a)
gcd(a,b,c)=gcd(a,b−a,c−b)
gcd(a1,a2,⋯,an)=gcd(a1,a2−a1,a3−a2,⋯,an−an−1)
所以在这里我们建树的时候存的是原数列的差分序列,从而来维护最大公约数。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+10; 4 int a[maxn]; 5 struct node 6 { 7 int L,R; 8 int mx,gcd; 9 int sum; 10 }tree[maxn<<2]; 11 void pushup(int u) 12 { 13 tree[u].mx=max(tree[u<<1].mx,tree[u<<1|1].mx); 14 tree[u].gcd=__gcd(tree[u<<1].gcd,tree[u<<1|1].gcd); 15 tree[u].sum=tree[u<<1].sum+tree[u<<1|1].sum; 16 } 17 void build(int l,int r,int root) 18 { 19 tree[root].L=l; tree[root].R=r; 20 if(l==r){ 21 tree[root].sum=a[l]; 22 tree[root].mx=max(a[l],-a[l]); 23 tree[root].gcd=max(a[l],-a[l]); 24 return; 25 } 26 int mid=l+r>>1; 27 build(l,mid,root<<1); 28 build(mid+1,r,root<<1|1); 29 pushup(root); 30 } 31 void update(int root,int pos,int val) 32 { 33 int L=tree[root].L;int R=tree[root].R; 34 if(L==R){ 35 a[L]+=val; 36 tree[root].sum=a[L]; 37 tree[root].mx=tree[root].gcd=max(a[L],-a[L]); 38 return; 39 } 40 int mid=L+R>>1; 41 if(pos<=mid) update(root<<1,pos,val); 42 else update(root<<1|1,pos,val); 43 pushup(root); 44 } 45 int query_max(int l,int r,int root) 46 { 47 int L=tree[root].L; int R=tree[root].R; 48 if(l<=L&&r>=R){ 49 return tree[root].mx; 50 } 51 int mid=L+R>>1; 52 int ans=0; 53 if(l<=mid) ans=max(ans,query_max(l,r,root<<1)); 54 if(r>mid) ans=max(ans,query_max(l,r,root<<1|1)); 55 return ans; 56 } 57 int query_sum(int l,int r,int root) 58 { 59 int L=tree[root].L; int R=tree[root].R; 60 if(l<=L&&r>=R){ 61 return tree[root].sum; 62 } 63 int mid=L+R>>1; 64 int ans=0; 65 if(l<=mid) ans+=query_sum(l,r,root<<1); 66 if(r>mid) ans+=query_sum(l,r,root<<1|1); 67 return ans; 68 } 69 int query_gcd(int l,int r,int root) 70 { 71 int L=tree[root].L; int R=tree[root].R; 72 if(l<=L&&r>=R){ 73 return tree[root].gcd; 74 } 75 int mid=L+R>>1; 76 int ans=0; 77 if(l<=mid) ans=__gcd(ans,query_gcd(l,r,root<<1)); 78 if(r>mid) ans=__gcd(ans,query_gcd(l,r,root<<1|1)); 79 return ans; 80 } 81 int main() 82 { 83 int n,m; 84 scanf("%d%d",&n,&m); 85 for(int i=1;i<=n;i++){ 86 scanf("%d",&a[i]); 87 } 88 for(int i=n;i>=1;i--){ 89 a[i]=a[i]-a[i-1]; 90 } 91 build(1,n,1); 92 while(m--){ 93 int op,l,r,x; 94 scanf("%d%d%d",&op,&l,&r); 95 if(op==1){ 96 scanf("%d",&x); 97 update(1,l,x); 98 if(r<=n-1) update(1,r+1,-x); 99 } 100 else if(op==2){ 101 if(l==r) printf("0\n"); 102 else{ 103 int ans=query_max(l+1,r,1); 104 printf("%d\n",ans); 105 } 106 } 107 else { 108 int tmp=query_sum(1,l,1); 109 if(r>l) tmp=__gcd(tmp,query_gcd(l+1,r,1)); 110 printf("%d\n",tmp); 111 } 112 } 113 }