一.树状数组是用 add(a[i]-a[i-1])引入差分,线段树怎么用呢?
线段树是用build()
二.本题属于Node多维护问题,应该注意啥
1.pushup()中要pushup多个变量
2.query()要返回Node,因为可能要用到节点里面多个变量值,
query()也是新版的query
这里放个旧版的query做对比:
三.一堆数的最大公约数有什么性质?
后面除了第一项,都是差分的形式b[i]=a[i]-a[i-1],
而第一项(原数组)可以用差分表示出来
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e5+10;
int n,m;
LL A[N];
struct Node
{
int l,r;
LL sum,d; //sum是区间数的和,d是最大公约数
}tr[N*4];
void pushup(Node &u,Node &l,Node &r)
{
u.sum=l.sum+r.sum;
u.d=gcd(l.d,r.d);
}
void pushup(int u)
{
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
LL gcd(LL a,LL b)
{
return b?gcd(b,a%b):a;
}
void build(int u,int l,int r)
{
tr[u]={l,r};
if(l==r)
{
LL b=A[r]-A[r-1]; //初始化差分
tr[u]={l,r,b,b};
return;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int x,LL v)
{
if(tr[u].l==x&&tr[u].r==x)
{
tr[u]={x,x,tr[u].sum+v,tr[u].sum+v};
}
else
{
int mid=(tr[u].l+tr[u].r)>>1;
if(x<=mid) modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u);
}
}
//用新版query()
Node query(int u,int l,int r) //只要Node 维护的是多维护,就返回Node
{
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) return query(u<<1,l,r);
//区间在右半边
else if(l>mid) return query(u<<1|1,l,r);
else //区间在左右两边
{
auto left=query(u<<1,l,r);
auto right=query(u<<1|1,l,r);
Node res;
pushup(res,left,right);
return res;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&A[i]);
}
build(1,1,n);
char op[2];
int l,r;
LL d;
while(m--)
{
scanf("%s%d%d",op,&l,&r);
if(*op=='Q') //查询
{
/*
[l,r]
由于查询的结果是
gcd(A[l],gcd(b[l+1],.....b[r]))
所以我们要查询两样东西:
①A[l] (left)
②gcd(b[l+1]....b[r]); (right)
*/
auto left=query(1,1,l); //1到l的sum
Node right({0,0,0,0});
if(l+1<=r)
right=query(1,l+1,r); //l+1~r的最大公约数
printf("%lld\n",abs(gcd(left.sum,right.d)));
}
else
{
scanf("%lld",&d);
modify(1,l,d);
if(r+1<=n)
modify(1,r+1,-d); //差分
}
}
return 0;
}