题意:有n个数字,q次询问,两种操作0 x y 把区间[x,y]全部开根号,1 x y 询问区间[x,y]数字和。
题解:q的范围到100000,题目中说数字最大2^63,所以一个数字最多开7次根就是1不再变化了,所以用一个线段树维护每个数字还能开多少次根号,区间维护最大值,如果某个区间的最大次数已经是0,就不用更新了。一直wa,然后发现有可能x > y,吐血。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 100005;
int n, q, num[N << 2], b[N];
ll arr[N], tree[N << 2], flag[N << 2];
void pushup(int k) {
tree[k] = tree[k * 2] + tree[k * 2 + 1];
num[k] = max(num[k * 2], num[k * 2 + 1]);
}
void build(int k, int left, int right) {
if (left == right) {
tree[k] = arr[left];
num[k] = b[left];
return;
}
int mid = (left + right) / 2;
build(k * 2, left, mid);
build(k * 2 + 1, mid + 1, right);
pushup(k);
}
void modify(int k, int left, int right, int l, int r) {
if (l <= left && right <= r) {
if (num[k] == 0) {
return;
}
if (left == right) {
tree[k] = sqrt(tree[k]);
num[k]--;
}
else {
int mid = (left + right) / 2;
if (l <= mid)
modify(k * 2, left, mid, l, r);
if (r > mid)
modify(k * 2 + 1, mid + 1, right, l, r);
pushup(k);
}
return;
}
int mid = (left + right) / 2;
if (l <= mid)
modify(k * 2, left, mid, l, r);
if (r > mid)
modify(k * 2 + 1, mid + 1, right, l, r);
pushup(k);
}
ll query(int k, int left, int right, int l, int r) {
if (l <= left && right <= r)
return tree[k];
int mid = (left + right) / 2;
ll res = 0;
if (l <= mid)
res += query(k * 2, left, mid, l, r);
if (r > mid)
res += query(k * 2 + 1, mid + 1, right, l, r);
pushup(k);
return res;
}
int main() {
int cas = 1;
while (scanf("%d", &n) == 1) {
for (int i = 1; i <= n; i++) {
scanf("%lld", &arr[i]);
ll temp = arr[i];
int cnt = 0;
while (temp != 1) {
cnt++;
temp = sqrt(temp);
}
b[i] = cnt;
}
build(1, 1, n);
scanf("%d", &q);
int op, l, r;
printf("Case #%d:\n", cas++);
while (q--) {
scanf("%d%d%d", &op, &l, &r);
if (l > r) swap(l, r);
if (op == 0)
modify(1, 1, n, l, r);
else {
printf("%lld\n", query(1, 1, n, l, r));
}
}
printf("\n");
}
return 0;
}