题目链接:Can you answer these queries?

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#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;
}