간격 GCD (선분 트리 수정은 간격 gcd 참조)

D. 간격 _GCD

파란색 책 제목 간격 GCD
제목 의미 : n 개의 숫자가 주어지면 Ai, M 작업 (N : 5e5, M : 5e5)에는 두 가지 작업이 있습니다.

  • C lrd는 A [l, r]에 d를 더합니다.
  • Q lr는 A [l, r]의 최대 공약수를 묻습니다.

사로 : gcd
(a 1, a 2, a 3, a 4..., an) = gcd ⁡ (a 1, a 2 − a 1, a 3 − a 2, a 4 − a 3... an − an − 1) \ gcd (a_1, a_2, a_3, a_4 ..., a_n) = \ gcd (a_1, a_2-a_1, a_3-a_2, a_4-a_3 ... a_n-a_ {n-1})g cd ( a(1),,3,4. . . ,N)=g cd ( a(1),(1),3,43. . . NN - 1)
그래서 : gcd ⁡ (al, al + 1, al + 2 ... Ar) = gcd (al, al + 1-al, al + 2-al + 1 ... Ar-ar-1) \ gcd ( a_l, a_ {l + 1}, a_ {l + 2} ... a_ {r}) = gcd (a_l, a_ {l + 1} -a_ {l}, a_ {l + 2} -a_ {l +1} ... a_ {r} -a_ {r-1})g cd ( a리터,L + 1,L + 2. . . R)=g c d ( a리터,L + 1리터,L + 2L + 1. . . RR - 1)
이러한 방식으로 유지되는 시퀀스는 원래 시퀀스 A에서 A의 차등 시퀀스 B로 변환됩니다., 이제 남은 것은 al a_l리터유지 보수의
장점은 다음과 같습니다.
각 작업 C (간격 추가) 에 대해 B 시퀀스의 ll에 대한 단일 수정 지점 만lr + 1 r + 1아르 자형+1 위치 (lll에 d 더하기;r + 1 r + 1아르 자형+1 빼기 d)


방법 : 차등 시퀀스 B를 유지하기 위해 선분 트리를 설정합니다 (차등 시퀀스 B의 gcd 를 계산하기 위해 ⁡ \ gcdg cd )
그루밍 번호 유지 보수 시리즈 A를 설정합니다 (al a_l을 계산하기 위해리터

struct SegmentTree{
    
    
    int l,r;
    LL dat;//计算gcd
    #define l(x) tree[x].l
    #define r(x) tree[x].r
    #define dat(x) tree[x].dat
}tree[maxn*4];
LL a[maxn],b[maxn],c[maxn];
int n,m;

void build(int p,int l,int r){
    
    
    l(p)=l,r(p)=r;
    if(l==r){
    
    dat(p)=b[l];return;}
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    dat(p)=lgcd(dat(p*2),dat(p*2+1));
}

/*void lazy(int p){
    if(add(p)){
        sum(p*2)+=add(p)*(r(p*2)-l(p*2)+1);
        sum(p*2+1)+=add(p)*(r(p*2+1)-l(p*2+1)+1);
        add(p*2)+=add(p);
        add(p*2+1)+=add(p);
        add(p)=0;
    }
}*/

void change(int p,int x,LL z){
    
    
    if(l(p)==r(p)){
    
    dat(p)+=z;return;}
    int mid=(l(p)+r(p))/2;
    if(x<=mid) change(p*2,x,z);
    else change(p*2+1,x,z);
    dat(p)=lgcd(dat(p*2),dat(p*2+1));
}

LL ask(int p,int l,int r){
    
    
    if(l<=l(p)&&r>=r(p)) return abs(dat(p));
    int mid=(l(p)+r(p))/2;
    LL ans=0ll;
    if(l<=mid)ans=lgcd(ans,ask(p*2,l,r));
    if(r>mid)ans=lgcd(ans,ask(2*p+1,l,r));
    return abs(ans);
}

void updata(int i,LL k){
    
    //在i位置上加上k
    while(i<=n){
    
    
        c[i]+=k;
        i+=i&(-i);
    }
}///区间[p,q]加c:updata(p,c);updata(q+1,-c);

LL getsum(int i){
    
    //求i的前缀和(1~i)
    LL res=0;
    while(i){
    
    
        res+=c[i];
        i-=i&(-i);
    }
    return res;
}///区间[p,q]:getsum(q)-getsum(p-1)

int main(){
    
    
    cin>>n>>m;
    memset(c,0,sizeof c);
    for(int i=1;i<=n;i++){
    
    cin>>a[i];b[i]=a[i]-a[i-1];}
    build(1,1,n);
    int x,y;
    LL z;
    char op;
    while(m--){
    
    
        cin>>op>>x>>y;
        if(op=='C'){
    
    
            cin>>z;
            updata(x,z);//维护数组A
            change(1,x,z);//维护数组B
            if(y+1<=n){
    
    change(1,y+1,-1ll*z);updata(y+1,-1l*z);}
        }else{
    
    cout<<lgcd(a[x]+getsum(x),ask(1,x+1,y))<<endl;}
    }
}

추천

출처blog.csdn.net/weixin_44986601/article/details/105759251