[Data Structure][线段树]BZOJ3211 花神游历各国

BZOJ3211: 花神游历各国

题意

给定$n$个数,$m$个操作,一种查询区间内所有数的和,一种将区间内所有数开根后取整。输出所有查询的结果。

题解

线段树。唯一的$trick$是由于这$n$个数的范围是$[0,10^9]$,最多开$5$次根就会达到$0$,于是我们开一个$cnt$数组记录一段区间是否需要继续开根即可。

代码

#include <bits/stdc++.h>
#define lson root << 1
#define rson root << 1 | 1
#define ltree lson, l, m
#define rtree rson, m + 1, r
using namespace std;
typedef long long ll;
const int N = 1e5+9;

ll data[N], sum[4 * N], cnt[4 * N];

inline int read() {
    int s = 1, x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return s * x;
}

inline void build(int root, int l, int r) {
    if (l == r) {
        sum[root] = data[l], cnt[root] = (data[l] <= 1); return;
    }
    int m = (l + r) >> 1;
    build(ltree);
    build(rtree);
    sum[root] = sum[lson] + sum[rson];
    cnt[root] = cnt[lson] + cnt[rson];
}

inline ll query(int root, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return sum[root];
    int m = (l + r) >> 1;
    ll ret = 0;
    if (ql <= m) ret += query(ltree, ql, qr);
    if (qr > m) ret += query(rtree, ql, qr);
    return ret;
}

inline void update(int root, int l, int r, int ql, int qr) {
    if (cnt[root] == r - l + 1) return;
    if (l == r) {
        sum[root] = (ll) sqrt(sum[root]), cnt[root] = (sum[root] <= 1); return;
    }
    int m = (l + r) >> 1;
    if (ql <= m) update(ltree, ql, qr);
    if (qr > m) update(rtree, ql, qr);
    sum[root] = sum[lson] + sum[rson];
    cnt[root] = cnt[lson] + cnt[rson];
}

int main() {
    int n = read();
    for (int i = 1; i <= n; i++) data[i] = read();
    build(1, 1, n);

    int m = read();
    for (int i = 1; i <= m; i++) {
        int x = read(), l = read(), r = read();
        if (x == 1) printf("%lld\n", query(1, 1, n, l, r));
        if (x == 2) update(1, 1, n, l, r);
    }
    return 0;
}
posted @ 2017-08-20 14:07  jstztzy  阅读(208)  评论(1编辑  收藏  举报