题意:多组数据,0 操作表示将区间开根号,1操作表示区间求和。因为开根号是向下取整,2^64开根号,不超过10次就变成1了,所以说N个元素最多操作10*N次,就可以直接输出结果了,不用再进行多余的操作了。本题值得注意的点是:题目输入的l和r不完全保证r必大于l,所以需要进行比较;然后就是只能点更新,不能成段更新比如 sqrt(a)+sqrt(b) != sqrt(a+b)。其他的就是基础操作了。
#include <cmath>
#include <cstdio>
#include <algorithm>
#define ll long long
const int maxn = 1e5+5;
ll sum[maxn<<2];
void Build(int i, int l, int r) {
if(l == r) {
scanf("%lld", &sum[i]);
return ;
}
int m = (l+r)>>1;
Build(i<<1, l, m);
Build(i<<1|1, m+1, r);
sum[i] = sum[i<<1] + sum[i<<1|1];
return ;
}
void Update(int i, int l, int r, int left, int right) {
if(left == right) {
sum[i] = sqrt(sum[i]);
return ;
}
if(l <= left && r >= right && sum[i] == (right-left+1)) return ;//这一步会减少很多时间(精髓)
int m = (left+right)>>1;
if(l <= m) Update(i<<1, l, r, left, m);
if(r > m) Update(i<<1|1, l, r, m+1, right);
sum[i] = sum[i<<1] + sum[i<<1|1];
return ;
}
ll Query(int i, int l, int r, int left, int right) {
if(l <= left && r >= right) {
return sum[i];
}
int m = (left+right)>>1;
ll sum = 0;
if(l <= m) sum += Query(i<<1, l, r, left, m);
if(r > m) sum += Query(i<<1|1, l, r, m+1, right);
return sum;
}
int main() {
int N, M;
int op, l, r;
int Case = 1;
while(~scanf("%d", &N)) {
Build(1, 1, N);
scanf("%d", &M);
printf("Case #%d:\n", Case++);
while(M--) {
scanf("%d %d %d", &op, &l, &r);
if(l > r) std::swap(l, r);
if(op == 0) {
Update(1, l, r, 1, N);
}
else {
printf("%lld\n", Query(1, l, r, 1, N));
}
}
printf("\n");
}
return 0;
}