https://www.acwing.com/problem/content/247/?time=1564925894116
给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
2、“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行表示M条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
N≤500000,M≤100000
输入样例:
5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4
输出样例:
1
2
4
思路:更相减损术是我国古代求两个数最大公因数的方法。有一结论:gcd(x,y,z,…)=gcd(x,y-x,z-y,…)。因此考虑用线段树来维护原数组的差分数组的gcd。(差分数组:dis[i]=a[i]-a[i-1])这样做有一个很大的好处,就是区间修改转化成了单点修改。证明如下:
根据定义我们有: d i s [ l ] = a [ l ] − a [ l − 1 ] , d i s [ l + 1 ] = a [ l + 1 ] − a [ l ] , … … dis[l]=a[l]-a[l-1],dis[l+1]=a[l+1]-a[l],…… dis[l]=a[l]−a[l−1],dis[l+1]=a[l+1]−a[l],…… … … , d i s [ r ] = a [ r ] − a [ r − 1 ] , d i s [ r + 1 ] = a [ r + 1 ] − a [ r ] ……,dis[r]=a[r]-a[r-1],dis[r+1]=a[r+1]-a[r] ……,dis[r]=a[r]−a[r−1],dis[r+1]=a[r+1]−a[r]那么对原序列的区间修改操作: ∑ k = l r ( a k = a k + d ) \sum_{k=l}^r(a_k=a_k+d) k=l∑r(ak=ak+d)实际上只影响到了线段树的两个节点: d i s [ l ] = d i s [ l ] + d dis[l]=dis[l]+d dis[l]=dis[l]+d d i s [ r + 1 ] = d i s [ r + 1 ] − d dis[r+1]=dis[r+1]-d dis[r+1]=dis[r+1]−d这样复杂度就可以接受了。注意单点修改的时候判断一下r+1与n的大小关系。修改的问题解决了,我们来看看查询的问题。要查询: G C D ( a [ l ] , a [ l + 1 ] , … … , a [ r ] ) GCD(a[l],a[l+1],……,a[r]) GCD(a[l],a[l+1],……,a[r])就相当于查询: G C D ( a [ l ] , a [ l + 1 ] − a [ l ] , … … , a [ r ] − a [ r − 1 ] ) GCD(a[l],a[l+1]-a[l],……,a[r]-a[r-1]) GCD(a[l],a[l+1]−a[l],……,a[r]−a[r−1])然而我们很容易写出一个线段树的区间查询操作: q u e r y ( l + 1 , r ) = G C D ( a [ l + 1 ] − a [ l ] , … … , a [ r ] − a [ r − 1 ] ) query(l+1,r)=GCD(a[l+1]-a[l],……,a[r]-a[r-1]) query(l+1,r)=GCD(a[l+1]−a[l],……,a[r]−a[r−1])因此我们知道知道a[l]的值就可以完成查询操作了。那么怎么维护原序列的值呢?用树状数组就可以了。原因很明显: d i s [ 1 ] + d i s [ 2 ] + … … + d i s [ i ] = a [ i ] dis[1]+dis[2]+……+dis[i]=a[i] dis[1]+dis[2]+……+dis[i]=a[i]那么问题就解决了,上代码。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
struct node
{
int l,r;
ll v;
}tree[maxn<<2];
int n,m;
ll a[maxn];
ll dis[maxn];
ll bit[maxn];
inline ll lowbit(ll x)
{
return x&(-x);
}
inline void add(int pos,ll v)
{
for(int i=pos;i<=n;i+=lowbit(i))
bit[i]+=v;
}
inline ll sum(int pos)
{
ll ans=0;
for(int i=pos;i;i-=lowbit(i))
ans+=bit[i];
return ans;
}
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
inline void up(int i)
{
tree[i].v=gcd(tree[i<<1].v,tree[i<<1|1].v);
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r;
if(l==r)
{
tree[i].v=dis[l];
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
up(i);
}
void update(int i,int pos,ll v)
{
if(tree[i].l==tree[i].r)
{
tree[i].v+=v;
return;
}
int mid=(tree[i].l+tree[i].r)>>1;
if(pos<=mid)
update(i<<1,pos,v);
else
update(i<<1|1,pos,v);
up(i);
}
ll query(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)
return tree[i].v;
int mid=(tree[i].l+tree[i].r)>>1;
if(r<=mid)
return query(i<<1,l,r);
else if(l>mid)
return query(i<<1|1,l,r);
else
return gcd(query(i<<1,l,mid),query(i<<1|1,mid+1,r));
}
inline void prework()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),dis[i]=a[i]-a[i-1];
for(int i=1;i<=n;i++)
add(i,dis[i]);
build(1,1,n);
}
inline void mainwork()
{
char op[10];
int l,r;
ll d;
for(int i=0;i<m;i++)
{
scanf("%s%d%d",op,&l,&r);
if(op[0]=='C')
{
scanf("%lld",&d);
add(l,d);
add(r+1,-d);
update(1,l,d);
if(r+1<=n)
update(1,r+1,-d);
}
else
printf("%lld\n",l==r?sum(l):gcd(abs(sum(l)),abs(query(1,l+1,r))));
}
}
int main()
{
prework();
mainwork();
}