interval GCD(线段树+差分)

本文介绍了一种利用数学性质gcd(x,y,z)=gcd(x,y-x,z-y)解决区间修改和求最大公约数问题的方法。通过差分数组和树状数组,实现了区间加法操作转化为单点修改,简化了复杂度。同时,文章提到了在实现过程中需要注意的边界条件,如防止越界。代码示例中展示了如何构建、修改和查询树状数组以求解问题。
摘要由CSDN通过智能技术生成

题目
大意:两个操作 一个操作是区间加上d 另一个是求区间的GCD
思路:首先如果区间修改使用lazytag的话 难以维护GCD 可以用数学性质gcd(x,y,z)=gcd(x,y-x,z-y)
使用差分 来将区间修改改为两个单点修改 只需要将b[l]+=d b[r+1]-=d 再维护一下gcd即可
query是为了求得区间gcd queryy是为了将差分数组复原为原值 即第一个数x 再做gcd
记得要用ll
还有一个坑点:小心越界

if(y+1<=n) change(1,y+1,-z);

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define endl '\n'
struct tree{
    ll l,r,val,sum;
}t[500050<<2];
ll n,m;
ll a[500050],b[500050];
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
void build(ll k,ll l,ll r)
{
    if(l==r) t[k]={l,r,b[l],b[l]};
    else{
        t[k]={l,r};
        ll mid=l+r>>1;
        build(k<<1,l,mid); build(k<<1|1,mid+1,r);
        t[k].val=gcd(t[k<<1].val,t[k<<1|1].val);
        t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
    }
}
void change(ll k,ll x,ll d)
{
    if(t[k].l==t[k].r&&t[k].l==x){
        t[k].val+=d; t[k].sum+=d;
        return;
    }
    ll mid=t[k].l+t[k].r>>1;
    if(x<=mid) change(k<<1,x,d);
    else change(k<<1|1,x,d);
    t[k].val=gcd(t[k<<1].val,t[k<<1|1].val);
    t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
ll query(ll k,ll l,ll r)
{
    if(t[k].l>=l&&t[k].r<=r){
        return t[k].val;
    }
    ll ans=0;
    ll mid=t[k].l+t[k].r>>1;
    if(l<=mid) ans=gcd(ans,query(k<<1,l,r));
    if(mid<r) ans=gcd(ans,query(k<<1|1,l,r));
    return ans; 
}
ll queryy(ll k,ll l,ll r)
{
    if(t[k].l>=l&&t[k].r<=r){
        return t[k].sum;
    }
    ll ans=0;
    ll mid=t[k].l+t[k].r>>1;
    if(l<=mid) ans+=queryy(k<<1,l,r);
    if(mid<r) ans+=queryy(k<<1|1,l,r);
    return ans; 
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>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);
for(int i=1;i<=m;i++){
    char ch; cin>>ch;
    if(ch=='Q'){
        ll x,y; cin>>x>>y;
        ll tmp=queryy(1,1,x);
        cout<<llabs(gcd(tmp,query(1,x+1,y)))<<endl;
    }
    else{
        ll x,y,z; cin>>x>>y>>z;
        change(1,x,z);
        if(y+1<=n) change(1,y+1,-z);
    }
}
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值