HDU 5381 The sum of gcd

传送门
线段树区间合并。。。
感谢:
http://www.cnblogs.com/CSU3901130321/p/4733701.html
http://www.cnblogs.com/crackpotisback/p/4737234.html
题目大意:

f(l,r)=gcd(ai,ai+1,....,aj)l<=i<=j<=r

这里首先要知道一个数的因子个数不超过 log2(n) 个,所以作为一个int整数来说,对应求得的最多只有31种gcd值

那么线段树上就可以维护这样的31种gcd值,并记录他们对应的数量

因为需要合并,所以要记录从左到右,和从右到左两种情况

这里我们其实很容易发现你一个个添加数的时候必然越往后区间段的gcd值越小,所以你保存进数组中对应的标号即为对应第几大的gcd值

#include <bits/stdc++.h>
using namespace std;
#define prt(k) cerr<<#k" = "<<k<<endl
typedef long long ll;
const int inf = 0x3f3f3f3f;
typedef pair<int, int> pii;

const int N = 10055;
#define gcd first
#define cnt second
struct node
{
    ll sum;
    vector<pii> l, r;
}tree[N<<2], ans;
#define PB push_back
#define MP make_pair
node Union(const node &a, const node &b) // 合并结点
{
    // a是b左侧区间的结点
    node ret;
    ret.sum = a.sum + b.sum;
    for (auto x : a.r) for (auto y : b.l) {
        ret.sum += 1ll * __gcd(x.gcd, y.gcd) * x.cnt * y.cnt;
    }
    ret.l = a.l; ret.r = b.r;
    // 更新从右侧出发的
    auto t = b.r.rbegin();
    int k = t->gcd;
    for (auto x: a.r) {
        int tmp = __gcd(k, x.gcd);
        if (tmp == ret.r.rbegin()->gcd)
            ret.r.rbegin()->cnt += x.cnt;
        else
            ret.r.PB(MP(tmp, x.cnt));
    }
    // 更新从左侧出发的
    t = a.l.rbegin();
    k = t->gcd;
    for (auto x : b.l) {
        int tmp = __gcd(k, x.gcd);
        if (tmp == ret.l.rbegin()->gcd)
            ret.l.rbegin()->cnt += x.cnt;
        else
            ret.l.PB(MP(tmp, x.cnt));
    }
    return ret;
}
void query(int o, int l, int r, int L, int R)
{
    if (L<=l && r<=R) {
        if (l==L) ans = tree[o];
        else ans = Union(ans, tree[o]);
        return ;
    }
    int  mid = (l + r) >> 1;
    if (L<=mid) query(o<<1, l, mid, L, R);
    if (mid < R) query((o<<1|1), mid+1, r, L, R);
    return;
}
void build(int o, int l, int r)
{
    tree[o].l.clear();
    tree[o].r.clear();
    if (l==r) {
        int x;
        scanf("%d", &x);
        pii p = make_pair(x, 1);
        tree[o].l.push_back(p);
        tree[o].r.push_back(p);
        tree[o].sum = x;
        return;
    }
    int mid = (l + r) >> 1;
    build(o<<1, l, mid);
    build((o<<1|1), mid+1, r);
    tree[o] = Union(tree[o<<1], tree[o<<1|1]);
    return;
}
void dfs(int o, int l, int r)
{
    printf("%d : [%d , %d]\n", o,l, r);
    for (auto p : tree[o].l) {
        printf("gcd : %d cnt : %d\n", p.gcd, p.cnt);
    }
    if (l==r)  return;
    int mid = (l + r) >> 1;
    dfs(o<<1, l, mid);
    dfs((o<<1|1), mid+1, r);
    return;
}
int main()
{
    int re, ca=1, n, m;
    scanf("%d", &re);
    while (re--) {
        scanf("%d", &n);
        build(1, 1, n);
       /// dfs(1, 1, n);
        scanf("%d", &m);
        while (m--) {
            int l, r;
            scanf("%d%d", &l, &r);
            query(1,1,n, l, r);
            printf("%I64d\n", ans.sum);
        }
    }
    return 0;
}

你可能感兴趣的:(线段树,区间合并)