BZOJ - 5028 -小Z的加油店(線段樹+區間更新+gcd)

題目:BZOJ - 5028

題解:

擴展裴蜀定理+差分+線段樹

求從l到r的最小能得到的油量就是求l~r範圍內a[i]的gcd

由性質gcd(a,b)=gcd(a,b-a)可得區間gcd可變爲:

gcd( a[l], a[l+1], a[l+2],..., a[r] ) = gcd( a[l], a[l+1] - a[l] , a[l+2] - a[l+1] ,..., a[r] - a[r-1] )。

下面談談如何證明:

由於 gcd 的性質: 
gcd(a, b) = gcd(a, a-b) 其中 a > b; 
簡單證明: 
令 d = gcd(a, b); 
a = d*t1; b = d*t2; 
兩式相減:a-b = d*(t1-t2),所以 gcd(a, a-b) = d 

這樣對[L, R]區間加, 只要L處加, R+1處減就可以了。 

下面再談談爲什麼只要處理L處加,R+1處減:

gcd(a1, a2 - a1, a3 - a2, a4 - a3, a5 - a4) 
因爲當對a1, a2, ….a4(L=1,R=4)處加v時, a1加上了v, 第二項(a2+v) - (a1+v)是不變的,(a3+v) - (a2+v),(a4+v) - (a3+v)也是不變,的,a5 - (a4+v)會少了一個(-v),所以就是說中間的差分是不變的,兩頭的差分需要更改,那麼就要在L處增加一個v,R+1處需要加上一個(-v),這樣就實現了L~R的增加操作,因爲我求的是差分的gcd,中間的值不會變就不需要更改了

當我們求gcd(l, l + 1, …r)的時候,只要求gcd(al, a(l+1) - al, a(l + 2) - a(l +1) ….ar - a(r - 1)); 
所以我們求gcd[l, r]只需要分佈求,先求得gcd[l + 1, r],再求1~l的和,因爲1~l的差分和就是al。

(比如說:L=3:a1,a2-a1,a3-a2,那麼a3的值就是前1~l的差分和)

所以總的看來,我這個題目只需要維護的就是區間的gcd跟和,然後區間更新L跟R+1位置的差分值,查詢1~L的差分和,查詢L+1~R的差分gcd,再求這兩個的gcd即可

代碼:

#include<bits/stdc++.h>
#define N 100005
#define L node<<1
#define R node<<1|1
using namespace std;
int n,m;
int a[N];
struct ljh
{
    int sum,g,l,r;
}e[N<<2];
int gcd(int a,int b)
{
    return (b==0)?a:gcd(b,a%b);
}
void pushup(int node)
{
    e[node].sum=(e[L].sum+e[R].sum);
    e[node].g=gcd(e[L].g,e[R].g);
}
void build(int node,int l,int r)
{
    e[node].l=l;
    e[node].r=r;
    if(l==r)
    {
        e[node].g=e[node].sum=a[l];
        return ;
    }
    int m=(l+r)>>1;
    build(L,l,m);
    build(R,m+1,r);
    pushup(node);
}   
void update(int node,int pos,int k)
{
    if(e[node].l==e[node].r)
    {
        e[node].g+=k;
        e[node].sum+=k;
        return ;
    }
    int m=(e[node].l+e[node].r)>>1;
    if(pos<=m)update(L,pos,k);
    else update(R,pos,k);
    pushup(node);
}
int query_g(int node,int x,int y)
{
    int ans=0;
    if(x<=e[node].l&&e[node].r<=y)
    {
        return e[node].g;
    }
    int m=(e[node].l+e[node].r)>>1;
    if(x<=m)ans=gcd(ans,query_g(L,x,y));
    if(y>m)ans=gcd(ans,query_g(R,x,y));
    return ans;
}
int query_s(int node,int x,int y)
{
    int ans=0;
    if(x<=e[node].l&&e[node].r<=y)
    {
        return e[node].sum;
    }
    int m=(e[node].l+e[node].r)>>1;
    if(x<=m)ans+=query_s(L,x,y);
    if(y>m)ans+=query_s(R,x,y);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=n;i>=2;i--)a[i]-=a[i-1];
    build(1,1,n);
    while(m--)
    {
        int op,x,y,z;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)
        {
            printf("%d\n",gcd(query_g(1,x+1,y),query_s(1,1,x)));
        }
        else
        {
            scanf("%d",&z);
            update(1,x,z);
            if(y<n)update(1,y+1,-z);
        }
    }
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章