树套树?这就是强者的世界吗!!!
大意
给定两个操作:
1、添加操作,将数组[l,r]范围内的数都加上d
2、询问操作,询问数组[l,r]范围内的最大公约数
每次询问输出一次答案
思路
1、区间修改只有加法操作,可使用差分去转换成单点修改。单点操作可不快乐多了!(懒标记雀食不好写啊!其实是写了懒标记发现超时了,蒟蒻本蒻。。。)
修改点L加d,点R+1减d。
存在定理(更相减损术):
gcd(a,b,c,d,e、、、)=gcd(a,b-a,c-b,d-c,e-d、、、)
刚好契合了差分数组。
树状数组维护差分数组可能更直观?这里顺手还是用的线段树。
2、区间查询
最大公约数怎么处理出来?
建个线段树维护前缀和,我们取差分数组前L个点的前缀和,根据上面的定理,将前缀和看作a求gcd。(这里还想了挺久的,果然是蒟蒻吗。。。)
注意差分数组内的数可能为负数,所以需对结果取绝对值
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=20100403;
#define N 500007
#define lch (k*2)
#define rch (k*2+1)
#define mid ((l+r)/2)
ll sum[N*4],gd[N*4],a[N];
ll n,m;
void init(ll k,ll l,ll r)
{
if(l>=r)
{
sum[k]=a[l]-a[l-1];
gd[k]=a[l]-a[l-1];
return;
}
init(lch,l,mid);
init(rch,mid+1,r);
sum[k]=sum[lch]+sum[rch];
gd[k]=__gcd(gd[lch],gd[rch]);
}
void update(ll k,ll l,ll r,ll tot,ll val)
{
if(l>=r)
{
sum[k]+=val;
gd[k]+=val;
return;
}
if(tot<=mid)
update(lch,l,mid,tot,val);
else
update(rch,mid+1,r,tot,val);
sum[k]=sum[lch]+sum[rch];
gd[k]=__gcd(gd[lch],gd[rch]);
}
ll qry_gcd(ll k,ll l,ll r,ll ql,ll qr)
{
if(ql<=l&&r<=qr)
{
return gd[k];
}
ll mx1=0,mx2=0;
if(ql<=mid)
{
mx1=qry_gcd(lch,l,mid,ql,qr);
}
if(mid+1<=qr)
{
mx2=qry_gcd(rch,mid+1,r,ql,qr);
}
return __gcd(mx1,mx2);
}
ll qry_sum(ll k,ll l,ll r,ll ql,ll qr)
{
if(ql<=l&&r<=qr)
{
return sum[k];
}
ll mx1=0,mx2=0;
if(ql<=mid)
{
mx1=qry_sum(lch,l,mid,ql,qr);
}
if(mid+1<=qr)
{
mx2=qry_sum(rch,mid+1,r,ql,qr);
}
return mx1+mx2;
}
int main()
{
ios::sync_with_stdio(0);
cin>>n>>m;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
}
init(1,1,n);
while(m--)
{
char op[3];
ll x,y,d;
cin>>op;
if(*op=='Q')
{
cin>>x>>y;
cout<<abs(__gcd(qry_sum(1,1,n,1,x),qry_gcd(1,1,n,x+1,y)))<<endl;
}
else
{
cin>>x>>y>>d;
update(1,1,n,x,d);
update(1,1,n,y+1,-d);
}
}
return 0;
}