题目链接:https://www.luogu.com.cn/problem/P4145
题目大意:
两种操作:
- 区间求和;
- 区间开方。
解题思路:
使用线段树解决这个问题。
但是区间更新(开方)需要懒惰标记。
但是这样也会TLE,所以还需要记录区间更新的次数的最小值,如果这个区间内的每个数都开方达到6次,那么这个区间内的数就都是1了。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, m;
long long sum[maxn<<2];
int lazy[maxn<<2], upd_times[maxn<<2];
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void push_up(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
upd_times[rt] = min(upd_times[rt<<1], upd_times[rt<<1|1]);
}
void push_down(int rt, int l, int r) {
if (lazy[rt]) {
if (l < r) {
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
}
else {
for (int i = 0; i < lazy[rt] && sum[rt] > 1; i ++)
sum[rt] = sqrt(sum[rt]);
}
upd_times[rt] += lazy[rt];
}
lazy[rt] = 0;
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%lld", &sum[rt]);
}
else {
int mid = (l + r) / 2;
build(lson);
build(rson);
push_up(rt);
}
}
void update(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
lazy[rt] ++;
return;
}
push_down(rt, l, r);
if (upd_times[rt] > 6) return;
int mid = (l + r) / 2;
if (L <= mid) update(L, R, lson);
if (R > mid) update(L, R, rson);
push_up(rt);
}
long long query(int L, int R, int l, int r, int rt) {
push_down(rt, l, r);
if (L <= l && r <= R && upd_times[rt] > 6) return r-l+1;
if (l == r) return sum[rt];
long long tmp = 0;
int mid = (l + r) / 2;
if (L <= mid) tmp += query(L, R, lson);
if (R > mid) tmp += query(L, R, rson);
push_up(rt);
return tmp;
}
int main() {
scanf("%d", &n);
build(1, n, 1);
scanf("%d", &m);
while (m --) {
int op, l, r;
scanf("%d%d%d", &op, &l, &r);
if (l > r) swap(l, r);
if (op == 0) {
update(l, r, 1, n, 1);
}
else {
printf("%lld
", query(l, r, 1, n, 1));
}
}
return 0;
}