HDU - 4027 Can you answer these queries?(线段树)

题目链接:Can you answer these queries?

题意:维护一个序列,支持两种操作:(1)将区间[l,r]内的所有数开根号(向下取整),(2)求区间[l,r]内元素的和

思路:和The Child and Sequence类似,对于操作(1),一个数最多开7次根号(向下取整)就会变为1,此时就不用再开根号了,所以我们用线段树维护区间和,如果一个区间和等于这个区间的长度,则表示这个区间内所有元素都为1,此时可以直接减枝,否则继续递归,直到叶子节点,然后开根号向下取整,其实也可以再维护一个区间最大值,如果最大值等于1,则可以直接减枝,对于操作(2),线段树区间查询区间和即可,注意可能有l>r,交换l,r即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 100010;

struct node {
    int l, r;
    ll v;
};

int n, m, icas;
node tr[4 * N];
ll c[N];

void build(int k, int l, int r)
{
    tr[k].l = l, tr[k].r = r;
    if (l == r) {
        tr[k].v = c[l];
        return;
    }
    int mid = (l + r) / 2;
    build(2 * k, l, mid);
    build(2 * k + 1, mid + 1, r);
    tr[k].v = tr[2 * k].v + tr[2 * k + 1].v;
}

void update(int k, int a, int b)
{
    if (tr[k].v == tr[k].r - tr[k].l + 1) return;
    if (tr[k].l == tr[k].r) {
        tr[k].v = sqrt(tr[k].v);
        return;
    }
    int mid = (tr[k].l + tr[k].r) / 2;
    if (a <= mid) update(2 * k, a, b);
    if (b > mid) update(2 * k + 1, a, b);
    tr[k].v = tr[2 * k].v + tr[2 * k + 1].v;
}

ll ask(int k, int a, int b)
{
    if (tr[k].l >= a && tr[k].r <= b) return tr[k].v;
    int mid = (tr[k].l + tr[k].r) / 2;
    ll res = 0;
    if (a <= mid) res += ask(2 * k, a, b);
    if (b > mid) res += ask(2 * k + 1, a, b);
    return res;
}

int main()
{
    while (scanf("%d", &n) != EOF) {
        for (int i = 1; i <= n; i++) scanf("%lld", &c[i]);
        build(1, 1, n);
        scanf("%d", &m);
        printf("Case #%d:\n", ++icas);
        while (m--) {
            int op, a, b;
            scanf("%d%d%d", &op, &a, &b);
            if (a > b) swap(a, b);
            if (0 == op) update(1, a, b);
            else printf("%lld\n", ask(1, a, b));
        }
        printf("\n");
    }
    return 0;
}

 

posted on 2020-04-25 17:19  啊啊鄂  阅读(151)  评论(0编辑  收藏  举报

导航