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;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/83625337
今日推荐