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;
}

时间: 2024-08-13 01:11:30

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

D - 小Z的加油店

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

bzoj 2038 小Z的袜子(hose)(莫队算法)

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 11542  Solved: 5166[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命--具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

BZOJ 2038 小z的袜子 &amp; 莫队算法(不就是个暴力么..)

题意: 给一段序列,询问一个区间,求出区间中.....woc! 贴原题! 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概

[BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)

Description Haruna每天都会给提督做早餐! 这天她发现早饭的食材被调皮的 Shimakaze放到了一棵 树上,每个结点都有一样食材,Shimakaze要考验一下她. 每个食材都有一个美味度,Shimakaze会进行两种操作: 1.修改某个结点的食材的美味度. 2.对于某条链,询问这条链的美味度集合中,最小的未出现的自然数是多少.即mex值. 请你帮帮Haruna吧. Solution 树上带修改莫队 统计答案的时候也分块查询,找到第一个没满的块开始一个一个找 #include<i

BZOJ 2038 小Z的袜子(hose) (莫队离线)

题目地址:BZOJ 2038 裸的莫队算法. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <stdio.h> #includ

bzoj 2038 小Z的袜子

好久没写题解了=_= ,整个暑假就没写过,还是决定写写吧,所以挑了这道大水题. 这是标准的莫队算法的问题,但由于可能数据水还是别的什么原因,不用曼哈顿最小生成树也可以过.具体就是按询问区间的左端点分块, 块内按右端点排序,然后暴力…… 真的是暴力,太暴力了,直到AC以后我才相信这么暴力真的可以在O(N^1.5)的时间复杂度内过掉. 块内具体就是右端点递增,左端点由于在块内并不是有序的,所以左端点就会晃来晃去,真是太暴力了…… 上代码: #include <cstdio> #include &l

bzoj 2038 小z的袜子 莫队例题

莫队,利用可以快速地通过一个问题的答案得到另一问题的答案这一特性,合理地组织问题的求解顺序,将已解决的问题帮助解决当前问题,来优化时间复杂度. 典型用法:处理静态(无修改)离线区间查询问题. 线段树也是处理区间问题的一个有力工具,它和莫队算法各有特点: 线段树可以支持修改,并且单次操作时间复杂度一般为O(log),支持在线,但是要求可以进行快速的区间合并操作,两个区间如不能快速合并(f(n)*O(log)>O(n)),则用线段树就没有什么实际价值了(暴力都比它块) 莫队算法可以解决某些线段树不能

BZOJ 2038 小Z的袜子(莫队算法)

莫队算法如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r-1]的答案,即可使用莫队算法.时间复杂度为O(n^1.5).如果只能在logn的时间移动区间,则时间复杂度是O(n^1.5*log n).其实就是找一个数据结构支持插入.删除时维护当前答案. 这道题的话我们很容易用数组来实现,做到O(1)的从[l,r]转移到[l,r+1]与[l+1,r]. 那么莫队算法怎么做呢?以下都是在转移为O(1)的基础下讨论的时间复杂度.另外由于n与m同阶,就统一写n.如果已知[l

BZOJ 2120 数颜色&amp;2453 维护队列 [带修改的莫队算法]【学习笔记】

题意: 询问区间中不同颜色的个数,单点修改颜色 发现以前写的学习笔记没法看,于是重写一下(不就是会用latex了嘛) 额外维护一个当前修改操作执行到的时间 如果要进行某个查询操作,修改操作的时间必须移动到这个查询操作处 按照$(pos[l], pos[r], tim)$排序 令$S=N^{\frac{2}{3}}$, 有$N^{\frac{1}{3}}$块 $l$移动$N*N^{\frac{2}{3}}$次 $r$移动$N*N^{\frac{1}{3}}+N*N^{\frac{2}{3}}$次