![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c29f4f5d6a71472b1d2337a950a922e5.png)
思路:
- 首先是线段树的做法
- 定义node节点,求公约数,那么定义d(记录区间公约数),还有左节点右节点
- 考虑定义一个数d,能不能维护好update
- 父节点的d = max(左树d,右树d),所有一个数d,够用
- 考虑区间加,可以使用差分数组b[i]=a[i]=a[i-1]
- 考虑区间求最大公约数(a,b,c)=(a,b-a,c-b)//公约数性质
- b-a,c-a可以看成差分数组
- a,b,c都加d,可以在差分数组a+=d,(c+1)-=d
- gcd(a1,a2,a3,a4)=gcd(a1,a2-a1,a3-a2,a4-a3)=gcd(a1, gcd(a2-a1,a3-a2,a4-a3))=gcd(a1, gcd(b2,b3,b4) )
- a1,a2,a3,a4都加d==b[1]+=d, b[4]-=d;
- 总之,一顿推公式得出:gcd(a1, gcd(b2,b3,b4) )
- a1,用数状数组求差分前缀和,后边的用线段数记录这个区间
- 更新的时候,树状数组差分维护一下,线段树维护一下
坑点
- 爆int
- 在数值加减的过程中可能会产生负数,而约定gcd是没有负数的,所以需要用这个式子来搞定负数。
代码
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N=6e5;
typedef long long ll;
ll a[N],b[N],c[N];
struct node{
int l,r;
ll d;
}tr[N*4];
int n,m;
//树
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lowbit(ll x)
{
return x&-x;
}
void add(ll x,ll cc)
{
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=cc;
}
ll sum(ll x)
{
ll res=0;
for(int i=x;i;i-=lowbit(i))
res+=c[i];
return res;
}
void pushup(node &u,node &l,node &r)
{
u.d=gcd(l.d,r.d);
}
void pushup(int u)
{
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
if(l==r) tr[u]={l,l,a[l]-a[l-1]};
else {
// tr[u]={l,r};
tr[u].l=l,tr[u].r=r;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void motify(int u, int x,ll c)
{
if(tr[u].l==tr[u].r){
tr[u].d+=c;
return;
}
else{
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) motify(u<<1,x,c);
else motify(u<<1|1,x,c);
pushup(u);
}
}
node query(int u,int l,int r)
{
if(tr[u].l>=l && tr[u].r<=r) return tr[u];
else{
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) query(u<<1,l,r);
else if(l>mid) query(u<<1|1,l,r);
else{
node t;
node left=query(u<<1,l,mid);
node right=query(u<<1|1,mid+1,r);
pushup(t,left,right);
return t;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) b[i]=a[i]-a[i-1];
build(1,1,n);
int l,r;
ll d;
char op[2];
while (m -- )
{
scanf("%s%d%d", op, &l, &r);
if (*op == 'Q')
{
ll left = b[l]+sum(l);
node right = query(1, l + 1, r);
printf("%lld\n", abs(gcd(left, right.d)));
}
else
{
scanf("%lld", &d);
add(l,d),add(r+1,-d);
motify(1, l, d);
if (r + 1 <= n) motify(1, r + 1, -d);
}
}
return 0;
}