BZOJ 5028 小Z的加油店

【题解】

  本题要求求出区间内的各个元素通过加减之后能够得出的最小的数,那么根据裴蜀定理可知答案就是区间内各个元素的最大公约数。

  那么本题题意化简成了维护一个序列,支持区间加上某个数以及查询区间元素的最大公约数。

  我们要证明这样一个定理:

    对于一个序列(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\n",gcd(qsum(l),query(1,l+1,r)));
54             else printf("%d\n",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 }
View Code

    

猜你喜欢

转载自www.cnblogs.com/DriverLao/p/9439913.html