题目链接在这里
题目描述:
有n个数,m次操作,每次操作给出x y z三个数,规则如下:
若x == 0,则将[ y , z ]区间的数开平方根(向下取整)
若x == 1,则将[ y , z ]区间的和求出来。
思路分析:
这是个并查集问题,每次更新的时候将子节点开平方就可以了。
但是值得注意的是,当值已经为1的时候,再开平方根已经没有意义了,所以优化一下,若某个节点值 == 最右下标 - 最左下标 + 1,即它的孩子的长度的话,就不用再往下进行了。
另外一个比较坑的点是,y有可能大于z,所以一定要判断一下大小。
代码如下:
#include
#include
#include
#include
#include
#define rep(i, n) for(int i = 0; i < n; ++i)
#define clr(x) memset(x, 0, sizeof(x))
#define rl (rt << 1)
#define rr (rt << 1 | 1)
#define ll long long
using namespace std;
const int MaxN = 2e5 + 10;
struct Node{
int l, r, mid;
ll val;
}tree[MaxN << 2];
int n, q;
ll ans;
void build(int l, int r, int rt){
tree[rt].l = l;
tree[rt].r = r;
tree[rt].mid = (l + r) >> 1;
if(l == r){
scanf("%lld", &tree[rt].val);
return;
}
build(l, tree[rt].mid, rl);
build(tree[rt].mid + 1, r, rr);
tree[rt].val = tree[rl].val + tree[rr].val;
}
void update(int l, int r, int rt){
//printf("l:%d, r:%d, rt:%d\n", l, r, rt);
if(tree[rt].val == tree[rt].r - tree[rt].l + 1) return;
if(tree[rt].l == tree[rt].r){
//printf("in, l:%d\n", l);
tree[rt].val = sqrt(tree[rt].val);
return;
}
if(r <= tree[rt].mid)
update(l, r, rl);
else if(l > tree[rt].mid)
update(l, r, rr);
else{
update(l, tree[rt].mid, rl);
update(tree[rt].mid + 1, r, rr);
}
tree[rt].val = tree[rl].val + tree[rr].val;
//printf("l:%d, r:%d, val:%lld, rt:%d\n", tree[rt].l, tree[rt].r, tree[rt].val, rt);
}
void query(int l, int r, int rt){
if(l <= tree[rt].l && tree[rt].r <= r){
//printf("l:%d, r:%d, val:%lld\n", tree[rt].l, tree[rt].r, tree[rt].val);
ans += tree[rt].val;
return;
}
if(r <= tree[rt].mid)
query(l, r, rl);
else if(l > tree[rt].mid)
query(l, r, rr);
else{
query(l, tree[rt].mid, rl);
query(tree[rt].mid + 1, r, rr);
}
}
int main(){
int Case = 1;
while(~scanf("%d", &n)){
build(1, n, 1);
scanf("%d", &q);
int x, y, z;
printf("Case #%d:\n", Case++);
while(q--){
scanf("%d %d %d", &x, &y, &z);
if(y > z){
y ^= z;
z ^= y;
y ^= z;
}
if(x){
ans = 0;
query(y, z, 1);
printf("%lld\n", ans);
}
else{
update(y, z, 1);
}
}
putchar('\n');
}
return 0;
}