1. 程式人生 > >HDU - 5381 The sum of gcd(離線處理 + 線段樹)

HDU - 5381 The sum of gcd(離線處理 + 線段樹)

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=5381

題目大意:給出一個長度為 n 的陣列a,現在有q次詢問,每次詢問給出一個區間 [L,R],要你求出這個區間內所有子區間的gcd之和,即求\sum_{i = L}^{R}\sum_{j = i+1}^{R}gcd(a_{i},a_{i+1},...,a_{j})

題目思路:根據gcd的性質,以L為起點,終點小於等於R的子區間所得到的不同的gcd個數不超過log(a_{L})個。所以對於一開始給出的每一個值a[i],我們就可以維護出以這個點為起點,終點從 i 到 n 的子區間的gcd值。

既然這樣,我們就可以維護一棵線段樹,線段樹中下標為 i 的結點表示以 i 為終點的所有子區間的gcd之和。同時倒著將a陣列內的值更新到線段樹中。在更新的同時我們還要將相同的gcd值進行合併,記錄每一段的資訊即可。

在查詢答案時,我們可以用一個vector來儲存查詢區間的左端點為L的所有區間,當更新到這個端點時,我們就對其所在的區間進行查詢,由於我們是倒著更新a陣列內的值,所以這個區間的右端點的值一定是已經被更新了的。

時間複雜度是O(n*log^2n)

具體實現看程式碼:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
const int MX = 1e4 + 5;

int n, q, _;
vector<pii>p[MX];
int a[MX];
ll sum[MX << 2], lazy[MX << 2], ans[MX];
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}
void push_up(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void push_down(int l, int r, int rt) {
    if (lazy[rt]) {
        int m = (l + r) >> 1;
        lazy[rt << 1] += lazy[rt];
        lazy[rt << 1 | 1] += lazy[rt];
        sum[rt << 1] += lazy[rt] * (m - l + 1);
        sum[rt << 1 | 1] += lazy[rt] * (r - m);
        lazy[rt] = 0;
    }
}
void build(int l, int r, int rt) {
    sum[rt] = lazy[rt] = 0;
    if (l == r) return;
    int m = (l + r) >> 1;
    build(lson); build(rson);
    push_up(rt);
}
void update(int L, int R, int d, int l, int r, int rt) {
    if (L <= l && r <= R) {
        sum[rt] += (ll)(r - l + 1) * d;
        lazy[rt] += d;
        return;
    }
    push_down(l, r, rt);
    int m = (l + r) >> 1;
    if (L <= m) update(L, R, d, lson);
    if (R > m) update(L, R, d, rson);
    push_up(rt);
}
ll query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return sum[rt];
    push_down(l, r, rt);
    ll res = 0;
    int m = (l + r) >> 1;
    if (L <= m) res += query(L, R, lson);
    if (R > m) res += query(L, R, rson);
    return res;
}
int g[MX], l[MX], r[MX];
int Merage(int cnt) {
    int j = 0;
    for (int i = 0; i < cnt; j++, i++) {
        g[j] = g[i]; l[j] = l[i]; r[j] = r[i];
        while (g[i] == g[i + 1]) {
            l[j] = min(l[j], l[i + 1]);
            r[j] = max(r[j], r[i + 1]);
            i++;
        }
    }
    return j;
}

int main() {
    // FIN;
    for (scanf("%d", &_); _; _--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]), p[i].clear();
        build(1, n, 1);
        scanf("%d", &q);
        for (int i = 1; i <= q; i++) {
            int L, R;
            scanf("%d%d", &L, &R);
            p[L].pb(MP(R, i));
        }
        int cnt = 0;
        for (int i = n; i >= 1; i--) {
            for (int j = 0; j < cnt; j++) {
                int x = gcd(g[j], a[i]);
                g[j] = x;
            }
            g[cnt] = a[i]; l[cnt] = r[cnt] = i; cnt++;
            cnt = Merage(cnt);
            for (int j = 0; j < cnt; j++) update(l[j], r[j], g[j], 1, n, 1);
            for (auto now : p[i])
                ans[now.se] = query(i, now.fi, 1, n, 1);
        }
        for (int i = 1; i <= q; i++)
            printf("%lld\n", ans[i]);
    }
    return 0;
}