大意是要求实现一个可以给区间每个数开根,同时区间求和的线段树。
由于开根后求和不等于求和后开根,所以每次更新都必须更新到根节点。所幸开根一个数到1之后再开根结果不变,所以如果发现某个线段的和等于r-l+1说明无需再更新。
由于开根操作是指数级别,每个区间被更新的最大次数不会很大,所以不会超时。
这题的输入似乎不保证l<=r,以后只要题目没提到l<=r就处理一下,防止出错。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#include <cmath>
using namespace std;
#define endl ‘\n‘
typedef long long ll;
const int N = 200000;
ll sum[N << 2];
ll lazy[N << 2];
void pushup(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt) {
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
lazy[rt] = 0;
}
void update(int l, int r, int L, int R, int rt) {
if(L <= l && R >= r) {
lazy[rt]++;
} else {
int mid = (l + r) / 2;
pushdown(rt);
if(L <= mid) update(l, mid, L, R, rt << 1);
if(R > mid) update(mid + 1, r, L, R, rt << 1 | 1);
pushup(rt);
}
}
ll query(int l, int r, int L, int R, int rt) {
ll res = 0;
if(L <= l && R >= r) {
if(sum[rt] == r - l + 1) return sum[rt];
}
if(l == r) {
int ti = lazy[rt];
while(ti--) {
sum[rt] = (ll)sqrt(sum[rt]);
}
lazy[rt] = 0;
return sum[rt];
}
pushdown(rt);
int mid = (l + r) / 2;
if(L <= mid) res += query(l, mid, L, R, rt << 1);
if(R > mid) res += query(mid + 1, r, L, R, rt << 1 | 1);
pushup(rt);
return res;
}
void build(int l, int r, int rt) {
if(l == r) {
cin >> sum[rt];
lazy[rt] = 0;
} else {
lazy[rt] = 0;
int mid = (l + r) / 2;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
pushup(rt);
}
}
int main() {
ios::sync_with_stdio(false);
int cases = 1;
int n;
while(cin >> n) {
cout << "Case #" << cases << ":" << endl;
build(1, n, 1);
int m;
cin >> m;
while(m--) {
int t;
int l, r;
cin >> t >> l >> r;
if(l > r) swap(l ,r);
if(t) {
cout << query(1, n, l, r, 1) << endl;
} else {
update(1, n, l, r, 1);
}
}
cout << endl;
cases++;
}
}
HDU4027 - Can you answer these queries? (线段树)
原文:https://www.cnblogs.com/limil/p/12741605.html