bzoj 3211 花神游历各国 线段树区间开方
题目大意
给定一个序列,支持区间开方、区间求和
\(n \leq 10^5,a_i \leq 10^9\)
思路
由于\(a_i \leq 10^9\),因此每一个元素至多开方五次就会变为1,因此每个点至多会被修改5次,复杂度\(O(nlogn)\)
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];
#define ll long long
struct node {
int l,r;
ll sum;
ll set;
}t[N << 2];
int read () {
int q = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-')f = -1;
ch = getchar();
}
while(isdigit(ch)) {
q = q * 10 + ch - '0';
ch = getchar();
}return q * f;
}
void update(int rt) {
int ch = rt << 1;
t[rt].sum = t[ch].sum + t[ch + 1].sum;
t[rt].set = (t[ch].set && t[ch + 1].set);
}
void build(int l,int r,int rt) {
t[rt].l = l;
t[rt].r = r;
if (l == r) {
t[rt].sum = a[l];
t[rt].set = (a[l] <= 1);
return;
}
int mid = (l + r) >> 1;
int ch = rt << 1;
build(l,mid,ch);
build(mid + 1,r,ch + 1);
update(rt);
}
void modify(int l,int r,int rt) {
if(t[rt].set) { //优化,如果当前不需要开方就不开了
return;
}
if(t[rt].l == t[rt].r) {
t[rt].sum = (ll)sqrt(t[rt].sum + 0.5);
t[rt].set = t[rt].sum <= 1;
}
else {
int mid = (t[rt].l + t[rt].r) >> 1;
int ch = rt << 1;
if(r <= mid) {
modify(l,r,ch);
}else if(l > mid) {
modify(l,r,ch + 1);
}else {
modify(l,mid,ch);
modify(mid + 1,r,ch + 1);
}
update(rt);
}
}
ll query(int l,int r,int rt) {
//cout << l << ' ' << r << endl;
if(l <= t[rt].l and t[rt].r <= r) return t[rt].sum;
int mid = (t[rt].l + t[rt].r) >> 1;
ll res = 0;
int ch = rt << 1;
if(l <= mid) {
res += query(l,r,ch);
}
if(r > mid) {
res += query(l,r,ch + 1);
}
return res;
}
int n,m;
int main () {
n = read();
for(int i = 1;i <= n; i ++) a[i] = read();
build(1,n,1);
m = read();
while(m --) {
int x,l,r;
x = read(),l = read(),r = read();
if(x == 1) {
printf("%lld\n",query(l,r,1));
}
else {
modify(l,r,1);
}
}
return 0;
}