题意:
问区间中的数加加减减能组成的正整数最小数。
题解:
其实是
ax+by+……+cz
的最小正整数值。
根据裴蜀定理,就是他们的gcd。
所以就成了维护区间gcd。
然而因为太弱,还是一脸蒙逼。
orz tkj大佬,要我差分后再做。
根据辗转相除法,差分后的gcd=原序的gcd。
于是只用求出序列第一个数和后面差分的gcd就可以了。
线段树维护。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
int n,m,v[100010],a[100010];
struct node{
int lc,rc,c,sum;
}tr[200010];int tot=0;
int gcd(int a,int b)
{
if(a==0) return b;
return gcd(b%a,a);
}
int bt(int l,int r)
{
int x=++tot;
if(l!=r)
{
int mid=(l+r)/2;
tr[x].lc=bt(l,mid);
tr[x].rc=bt(mid+1,r);
tr[x].c=gcd(tr[tr[x].lc].c,tr[tr[x].rc].c);
tr[x].sum=tr[tr[x].lc].sum+tr[tr[x].rc].sum;
}
else tr[x].c=tr[x].sum=a[l];
return x;
}
void change(int x,int l,int r,int k,int c)
{
if(l==r){tr[x].c+=c;tr[x].sum+=c;return;}
int mid=(l+r)/2;
if(k<=mid) change(tr[x].lc,l,mid,k,c);
else change(tr[x].rc,mid+1,r,k,c);
tr[x].c=gcd(tr[tr[x].lc].c,tr[tr[x].rc].c);
tr[x].sum=tr[tr[x].lc].sum+tr[tr[x].rc].sum;
}
int findans(int x,int l,int r,int fl,int fr)
{
if(fl==l&&fr==r) return tr[x].c;
int mid=(l+r)/2;
if(fr<=mid) return findans(tr[x].lc,l,mid,fl,fr);
if(fl>mid) return findans(tr[x].rc,mid+1,r,fl,fr);
return gcd(findans(tr[x].lc,l,mid,fl,mid),findans(tr[x].rc,mid+1,r,mid+1,fr));
}
int findsum(int x,int l,int r,int fl,int fr)
{
if(fl==l&&fr==r) return tr[x].sum;
int mid=(l+r)/2;
if(fr<=mid) return findsum(tr[x].lc,l,mid,fl,fr);
if(fl>mid) return findsum(tr[x].rc,mid+1,r,fl,fr);
return findsum(tr[x].lc,l,mid,fl,mid)+findsum(tr[x].rc,mid+1,r,mid+1,fr);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
for(int i=1;i<=n;i++)
a[i]=v[i]-v[i-1];
bt(1,n);
while(m--)
{
int tmp;scanf("%d",&tmp);
if(tmp==1)
{
int l,r;scanf("%d %d",&l,&r);
if(l==r) printf("%d\n",findsum(1,1,n,1,l));
else printf("%d\n",abs(gcd(findans(1,1,n,l+1,r),findsum(1,1,n,1,l))));
}
else
{
int l,r,c;scanf("%d %d %d",&l,&r,&c);
change(1,1,n,l,c);
if(r!=n) change(1,1,n,r+1,-c);
}
}
}