【题解】
本题要求求出区间内的各个元素通过加减之后能够得出的最小的数,那么根据裴蜀定理可知答案就是区间内各个元素的最大公约数。
那么本题题意化简成了维护一个序列,支持区间加上某个数以及查询区间元素的最大公约数。
我们要证明这样一个定理:
对于一个序列(a,b,c,d,...),gcd(a,b,c,d,...)=gcd(a,b-a,c-b,d-c,...),文字表述就是原序列的最大公约数等于序列差分后的最大公约数。
证明方法如下:
1,设t为序列(a,b,c,d,...)的公约数,a<b<c,b=k1*a+x1,c=k2*a+x2,
那么我们有t|a,t|b,t|c
所以有t|x1,t|x2
所以t|(x2-x1)
所以t|(k1-1)*a+x1,t|(k2-k1)*a+(x2-x1)
即t也是序列(a,b-a,c-b,...)的公约数
同理,(a,b,c,...)的任一公约数也是(a,b-a,c-b,...)的公约数。
2,设t为序列(a,b-a,c-b,...)的公约数,
那么有t|a, t|(k1-1)*a+x1, t|(k2-k1)*a+(x2-x1)
所以有t|x1,t|x2-x1
所以t|x2
所以t|a, t|k1*a+x1,t|k2*a+x2
即t也是序列(a,b,c,...)的公约数
同理,(a,b-a,c-b,...)的任一公约数也是(a,b,c,...)的公约数。
综上,gcd(a,b,c,...)=gcd(a,b-a,c-b,...).
证明完这个定理之后,我们就可以把题意化为求区间第一个元素与后面元素的差分值的GCD
我们先把原序列差分,线段树维护差分数组的GCD。因为我们已经进行了差分,所以区间加操作变成了点修改,可以直接在线段树上logn完成。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 #define rg register 6 #define N 100010 7 #define ls (u<<1) 8 #define rs (u<<1|1) 9 #define mid ((a[u].l+a[u].r)>>1) 10 using namespace std; 11 int n,m,v[N],c[N],t[N]; 12 struct tree{ 13 int l,r,g; 14 }a[N<<2]; 15 inline int read(){ 16 int k=0,f=1; char c=getchar(); 17 while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); 18 while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar(); 19 return k*f; 20 } 21 int gcd(int x,int y){return y?gcd(y,x%y):x;} 22 void build(int u,int l,int r){ 23 a[u].l=l; a[u].r=r; 24 if(l<r) build(ls,l,mid),build(rs,mid+1,r),a[u].g=gcd(a[ls].g,a[rs].g); 25 else a[u].g=abs(c[l]); 26 } 27 void update(int u,int pos,int data){ 28 if(a[u].l==a[u].r){ 29 a[u].g=data; return; 30 } 31 update(pos<=mid?ls:rs,pos,data); 32 a[u].g=gcd(a[ls].g,a[rs].g); 33 } 34 int query(int u,int l,int r){ 35 if(l<=a[u].l&&a[u].r<=r) return a[u].g; 36 int ret=0; bool goleft=0; 37 if(l<=mid) ret=query(ls,l,r),goleft=1; 38 if(r>mid){ 39 if(goleft) ret=gcd(ret,query(rs,l,r)); 40 else ret=query(rs,l,r); 41 } 42 return ret; 43 } 44 inline void add(int x,int y){for(;x<=n+10;x+=(x&-x)) t[x]+=y;} 45 inline int qsum(int x){int ret=0; for(;x;x-=(x&-x)) ret+=t[x]; return ret;} 46 int main(){ 47 n=read(); m=read(); 48 for(rg int i=1;i<=n;i++) v[i]=read(),c[i]=v[i]-v[i-1],add(i,c[i]); 49 build(1,1,n); 50 while(m--){ 51 int opt=read(),l=read(),r=read(); if(l>r) swap(l,r); 52 if(opt==1){ 53 if(l<r) printf("%d ",gcd(qsum(l),query(1,l+1,r))); 54 else printf("%d ",qsum(l)); 55 } 56 else{ 57 int del=read(); 58 c[l]+=del; c[r+1]-=del; 59 add(l,del); if(r<n) add(r+1,-del); 60 update(1,l,abs(c[l])); if(r<n) update(1,r+1,abs(c[r+1])); 61 } 62 } 63 return 0; 64 }