1. 程式人生 > >BZOJ 5028 小Z的加油店

BZOJ 5028 小Z的加油店

opened lose \n nbsp 個數 getch abs http reg

技術分享圖片

【題解】

  本題要求求出區間內的各個元素通過加減之後能夠得出的最小的數,那麽根據裴蜀定理可知答案就是區間內各個元素的最大公約數。

  那麽本題題意化簡成了維護一個序列,支持區間加上某個數以及查詢區間元素的最大公約數。

  我們要證明這樣一個定理:

    對於一個序列(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

    

BZOJ 5028 小Z的加油店