bzoj 5028: 小Z的加油店——带修改的区间gcd

Description

小Z经营一家加油店。小Z加油的方式非常奇怪。他有一排瓶子,每个瓶子有一个容量vi。每次别人来加油,他会让
别人选连续一段的瓶子。他可以用这些瓶子装汽油,但他只有三种操作:
1.把一个瓶子完全加满;
2.把一个瓶子完全倒空;
3.把一个瓶子里的汽油倒进另一个瓶子,直到倒出瓶子空了或者倒进的瓶子满了。
当然,为了回馈用户,小Z会时不时选择连续一段瓶子,给每个瓶子容积都增加x。
为了尽可能给更多的人加油,每次客户来加油他都想知道他能够倒腾出的汽油量最少是多少?
当然他不会一点汽油都不给客户。

Input

第一行包括两个数字:瓶子数n,事件数m。
第二行包含n个整数,表示每个瓶子的容量vi。
接下来m行,每行先有三个整数fi li ri。
若fi=1表示询问li到ri他最少能倒腾出的汽油量最少是多少?
若fi=2 再读入一个整数x。表示他将li到ri的瓶子容量都增加了x。
1 <= n,m <= 10^5 , 1<=li<=ri<=n , 1<=初始容量,增加的容量<=1000

Output

对于每个询问输出对应的答案

Sample Input

3 4
2 3 4
1 1 3
2 2 2 1
1 1 3
1 2 3

Sample Output

1
2
4

HINT

 有可能出现L>R

——————————————————————————————

考虑一下更相减损术 题目就转换成了求区间gcd(带修改

这是一波套路题 考虑gcd(a,b,c,d,e)=gcd(a-b,b-c,c-d,d-e,e)

所以我们可以维护一下差分 也就是类似a-b这样的东西

这样之后区间l->r +v 就变成了 l-1 - v  r +v

当然注意最后的e是不带差分的 所以还要维护一下原序列 方便查询右端点r

所以需要的操作就是 维护原序列的差分(单点修改区间查gcd)和原序列本身(区间加单点查询)

 

#include<cstdio>
#include<cstring>
#include<algorithm>
const int M=1e5+7;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
    return ans*f;
} 
int n,m,p;
int N,g[3*M],bit[M],h[M];
int gcd(int x,int y){
    while(y){p=x%y; x=y; y=p;}
    return x;
}
void tr_modify(int x,int v){
    g[x+=N]+=v;
    for(x>>=1;x;x>>=1) g[x]=gcd(g[x<<1],g[x<<1^1]);
}
#define lowbit(x) x&-x
int s[M];
void bit_insert(int x,int v){
    while(x<=n){
        s[x]+=v; 
        x+=lowbit(x);
    }
}
int bit_query(int x){
    int ans=h[x];
    while(x) ans+=s[x],x-=lowbit(x);
    return ans;
}
void modify(int l,int r,int v){
    bit_insert(l,v); tr_modify(l-1,-v);
    bit_insert(r+1,-v); tr_modify(r,v);
}
int push_ans(int l,int r){
    int ans=bit_query(r);
    for(l=l+N-1,r=r+N;r-l!=1;l>>=1,r>>=1){
        if(~l&1) ans=gcd(ans,g[l^1]);
        if(r&1) ans=gcd(ans,g[r^1]);
    }
    if(ans<0) ans=-ans;
    return ans;
}
void pd(int &x,int &y){if(x>y) std::swap(x,y);}
int main(){
    int k,l,r,v;
    n=read(); m=read();
    for(int i=1;i<=n;i++) h[i]=read();
    for(N=1;N<=n+5;N<<=1);
    for(int i=1;i<n;i++) g[i+N]=h[i]-h[i+1];
    for(int i=N-1;i;i--) g[i]=gcd(g[i<<1],g[i<<1^1]);
    for(int i=1;i<=m;i++){
        k=read(); 
        if(k==1) l=read(),r=read(),pd(l,r),printf("%d\n",push_ans(l,r));
        else l=read(),r=read(),v=read(),pd(l,r),modify(l,r,v);
    }
    return 0;
}
View Code

 

 

 

posted @ 2017-09-10 12:51  友人Aqwq  阅读(335)  评论(0编辑  收藏  举报