题目链接: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: ", ++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 ", ask(1, a, b)); } printf(" "); } return 0; }