<h1>题目描述</h1>
题目链接
小Z经营一家加油店。小Z加油的方式非常奇怪。他有一排瓶子,每个瓶子有一个容量vi。每次别人来加油,他会让
别人选连续一段的瓶子。他可以用这些瓶子装汽油,但他只有三种操作:
- 把一个瓶子完全加满;
- 把一个瓶子完全倒空;
- 把一个瓶子里的汽油倒进另一个瓶子,直到倒出瓶子空了或者倒进的瓶子满了。
当然,为了回馈用户,小Z会时不时选择连续一段瓶子,给每个瓶子容积都增加x。
为了尽可能给更多的人加油,每次客户来加油他都想知道他能够倒腾出的汽油量最少是多少?
当然他不会一点汽油都不给客户。
<h1>解题报告</h1>
首先考虑只有询问怎么做。
一个显然的结论是:能表示出来的最小的数字一定是这些数字的gcd。
所以只有询问的话倍增处理一下gcd就可以了,时间复杂度 $O(nlog^2n)$
然后考虑加入了修改怎么做。
如果只有单点修改还是比较好做的,用线段树维护一下区间gcd,每次修改和查询都是 $O(log^2n)$ 的,总复杂度 $O(nlog^2n)$。
但是这里要求是区间修改,怎么办呢?
我们考虑将这个区间修改变成单点修改可以解决的问题,注意到 $\text{gcd}(a,b) = \text{gcd}(b,a-b)$,所以区间 $[l,r]$ 的 gcd 等价于将这个区间差分后的 gcd。
于是我们用线段树维护一下差分后的数组的区间 gcd 和区间和,支持单点修改和区间查询即可。
时间复杂度 $O(nlog^2n)$
<h1>代码</h1>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define Re register
#define LL long long
#define U unsigned
#define FOR(i,a,b) for(Re int i = a;i <= b;++i)
#define ROF(i,a,b) for(Re int i = a;i >= b;--i)
#define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c)
#define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c)
#define CLR(i,a) memset(i,a,sizeof(i))
#define BR printf("--------------------n")
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
#define int LL
const int MAXN = 100000+5;
struct SegmentTree New(int ,int ,SegmentTree ,SegmentTree *);
int _gcd(int a,int b){
return (!b) ? a : _gcd(b,a%b);
}
struct SegmentTree{
int l,r,gcd,sum;
SegmentTree lc,rc;
static SegmentTree *build(int l,int r){
int mid = (l + r) >> 1;
return (l == r) ? New(l,r,NULL,NULL) : New(l,r,build(l,mid),build(mid+1,r));
}
void pushup(){
gcd = _gcd(lc->gcd,rc->gcd);
sum = lc->sum + rc->sum;
}
void update(int pos,int x){
if(l == r){
gcd += x;sum += x;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid) lc->update(pos,x);
else rc->update(pos,x);
pushup();
}
int querygcd(int L,int R){
if(L > R) return 0;
if(L == l && R == r) return gcd;
int mid = (l + r) >> 1;
if(R <= mid) return lc->querygcd(L,R);
if(L > mid) return rc->querygcd(L,R);
return _gcd(lc->querygcd(L,mid),rc->querygcd(mid+1,R));
}
int querysum(int L,int R){
//if(L > R) return 0;
if(L == l && R == r) return sum;
int mid = (l + r) >> 1;
if(R <= mid) return lc->querysum(L,R);
if(L > mid) return rc->querysum(L,R);
return lc->querysum(L,mid)+rc->querysum(mid+1,R);
}
}pool[MAXN<<2],frog = pool,segt;
SegmentTree New(int l,int r,SegmentTree lc,SegmentTree *rc){
SegmentTree *ret = ++frog;
ret->l = l;ret->r = r;
ret->lc = lc;ret->rc = rc;
ret->gcd = 0;ret->sum = 0;
return ret;
}
int N,M;
int a[MAXN],c[MAXN];
signed main(){
scanf("%lld%lld",&N,&M);
segt = SegmentTree::build(1,N);
FOR(i,1,N){
scanf("%lld",a+i);
segt->update(i,a[i]-a[i-1]);
}
while(M--){
int opt,l,r;scanf("%lld%lld%lld",&opt,&l,&r);
if(opt == 1) printf("%ldn",std::labs(_gcd(segt->querysum(1,l),segt->querygcd(l+1,r))));
else{
int x;scanf("%lld",&x);
segt->update(l,x);
if(r < N) segt->update(r+1,-x);
}
}
return 0;
}
/*
3 4
2 3 4
1 1 3
2 2 2 1
1 1 3
1 2 3
*/