AcWing 246. 区间最大公约数

传送门

题目描述

给定一个长度为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显然可以用线段树来做。同时题目还需要区间更新。

   我们知道gcd(x,y) = gxd (x,y-x)。它可以进一步扩展到三个数的情况:gcd(x,y,z) = gcd(x,y-x,z-y)。因此我们可以构造一个长度为N的新数列B,其中B[i] = A[i] - A][i-1]。数列B为A的差分序列。用线段树维护序列B的区间GCD就相当于维护A的区间GCD。这样一来询问[l,r]的GCD就相当于求gcd(A[l],B[l+1,r]的gcd),更新[l,r]只需在B的线段树上更新l和r+1这两个点即可(注意r+1<=n),同时还要修改A中的值,这时我们可以用支持“区间修改、单点查询”的树状数组来维护。

代码:

AcWing 246. 区间最大公约数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 5e5 + 10;
struct node{
    ll l,r,gcd;
}T[N<<2];
ll a[N],b[N],c[N];
void up(int rt) {
    T[rt].gcd = __gcd(T[rt<<1].gcd,T[rt<<1|1].gcd);
}
void build(int rt,ll l,ll r) {
    T[rt].l = l,T[rt].r = r;
    if (l == r) {
        T[rt].gcd = b[l];
        return;
    }
    ll mid = (l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    up(rt);
}
void update(int rt,ll x,ll val) {
    if (T[rt].l == T[rt].r && T[rt].l == x) {
        T[rt].gcd += val;
        return;
    }
    ll mid = (T[rt].l + T[rt].r)>>1;
    if (x <= mid) update(rt<<1,x,val);
    else update(rt<<1|1,x,val);
    up(rt);
}
ll ask(int rt,ll l,ll r){
    if (l <= T[rt].l && r >= T[rt].r) return abs(T[rt].gcd);
    ll mid = (T[rt].l+T[rt].r)>>1;
    ll ans1 = 0,ans2 = 0;
    if (l<=mid) ans1 = ask(rt<<1,l,r);
    if (r>mid) ans2 = ask(rt<<1|1,l,r);
    return abs(__gcd(ans1,ans2));
}
ll lowbit(ll x) { return x&(-x); }
void add(ll x,ll val) {
    for (;x<N;x+=lowbit(x)) c[x]+=val;
}
ll query(ll x) {
    ll ans = 0;
    for (;x>0;x-=lowbit(x)) ans += c[x];
    return ans;
}
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; i++) {
        scanf("%lld",&a[i]);
        b[i] = a[i] - a[i-1];
    }
    build(1,1,n);
    while (m--) {
        char op[5];
        ll l,r,x;
        scanf("%s%lld%lld",op,&l,&r);
        if (op[0] == 'Q') printf("%lld\n",__gcd(a[l]+query(l),ask(1,l+1,r)));
        else {
            scanf("%lld",&x);
            update(1,l,x);
            if (r<n) update(1,r+1,-x);
            add(l,x);
            add(r+1,-x);
        }
    }
    return 0;
}
View Code

 

上一篇:java的几种验证


下一篇:迷你图——别看我个头小,却是图形显示的利器!