题意
小Z经营一家加油店。小Z加油的方式非常奇怪。他有一排瓶子,每个瓶子有一个容量vi。每次别人来加油,他会让
别人选连续一段的瓶子。他可以用这些瓶子装汽油,但他只有三种操作:
1.把一个瓶子完全加满;
2.把一个瓶子完全倒空;
3.把一个瓶子里的汽油倒进另一个瓶子,直到倒出瓶子空了或者倒进的瓶子满了。
当然,为了回馈用户,小Z会时不时选择连续一段瓶子,给每个瓶子容积都增加x。
为了尽可能给更多的人加油,每次客户来加油他都想知道他能够倒腾出的汽油量最少是多少?
当然他不会一点汽油都不给客户。
1 <= n,m <= 10^5 , 1<=li<=ri<=n , 1<=初始容量,增加的容量<=1000
分析
题目显然就是要我们维护区间加和求区间gcd。
把序列查分一下,就变成了单点修改和区间查询,直接线段树维护即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=100005;
int n,m,a[N];
struct tree{int d,s;}t[N*4];
int gcd(int x,int y)
{
if (!y) return x;
else return gcd(y,x%y);
}
void updata(int d)
{
t[d].s=t[d*2].s+t[d*2+1].s;
t[d].d=gcd(t[d*2].d,t[d*2+1].d);
}
void build(int d,int l,int r)
{
if (l==r) {t[d].d=t[d].s=a[l];return;}
int mid=(l+r)/2;
build(d*2,l,mid);build(d*2+1,mid+1,r);
updata(d);
}
void ins(int d,int l,int r,int x,int y)
{
if (l==r) {t[d].s+=y;t[d].d+=y;return;}
int mid=(l+r)/2;
if (x<=mid) ins(d*2,l,mid,x,y);
else ins(d*2+1,mid+1,r,x,y);
updata(d);
}
int query(int d,int l,int r,int x,int y)
{
if (x>y) return 0;
if (x<=l&&r<=y) return t[d].d;
int mid=(l+r)/2;
if (y<=mid) return query(d*2,l,mid,x,y);
else if (x>mid) return query(d*2+1,mid+1,r,x,y);
else return gcd(query(d*2,l,mid,x,mid),query(d*2+1,mid+1,r,mid+1,y));
}
int find(int d,int l,int r,int x,int y)
{
if (x<=l&&r<=y) return t[d].s;
int mid=(l+r)/2;
if (y<=mid) return find(d*2,l,mid,x,y);
else if (x>mid) return find(d*2+1,mid+1,r,x,y);
else return find(d*2,l,mid,x,mid)+find(d*2+1,mid+1,r,mid+1,y);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=n;i>1;i--) a[i]-=a[i-1];
build(1,1,n);
while (m--)
{
int op,l,r;scanf("%d%d%d",&op,&l,&r);
if (op==1) printf("%d\n",abs(gcd(query(1,1,n,l+1,r),find(1,1,n,1,l))));
else
{
int x;scanf("%d",&x);
ins(1,1,n,l,x);
if (r<n) ins(1,1,n,r+1,-x);
}
}
return 0;
}