这个题目camp里面有讲过,用到的是BOZJ 3211
对于给定的,n个数的序列,我们定义两个操作,分别是区间开根号以及区间求和。共有m次查询
其中 n,m≤1e5,∑ni=1ai≤1e18 n , m ≤ 1 e 5 , ∑ i = 1 n a i ≤ 1 e 18
我们可以简单的计算一下,一个 1e18 1 e 18 以内的数,经过最多6次开平方操作后,会变成1。那么也就是说,我们如果建立了线段树,对于每一个叶子节点我们最多进行6次的更新操作后,每次就不会改变大小了。那么我们可以通过维护区间的最大值和区间和,对于区间最大值是1的区间,我们就可以直接不考虑了。因为对其中所有数做开平方操作,都不会改变区间的数,以及区间和。
那么整理思路,我们就是维护区间和及区间最大值,对于最大值等于1的区间,我们不用进行开平方操作,对于需要进行开平方的区间,我们一直更新到叶子节点,因为每个叶子节点最多更新6次,所以这个是可以接受的,时间复杂度 O(6nlogn) O ( 6 n log n ) 即 O(nlogn) O ( n log n )
struct Node{
int l, r;
ll sum, maxx; //特征值
int lazy;
}tree[maxn<<2]; //开四倍空间
ll num[maxn]; //数值数组
void build(int i, int l, int r){
tree[i].l = l;
tree[i].r = r;
if(l == r){
tree[i].maxx = num[l];
tree[i].sum = num[l];
return ;
}
int mid = (l+r)>>1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r);
tree[i].sum = tree[i<<1].sum+tree[i<<1|1].sum;
tree[i].maxx = max(tree[i<<1].maxx, tree[i<<1|1].maxx);
}
void update(int i, int l, int r){
int mid = (tree[i].l+tree[i].r)>>1;
if(tree[i].l == tree[i].r){
tree[i].maxx = sqrt(tree[i].maxx);
tree[i].sum = sqrt(tree[i].sum);
return ;
}
if(tree[i].maxx == 1)
return ;
if(r <= mid) update(i<<1, l, r);
else if(l > mid) update(i<<1|1, l, r);
else {
update(i<<1, l, mid);
update(i<<1|1, mid+1, r);
}
tree[i].sum = tree[i<<1].sum + tree[i<<1|1].sum;
tree[i].maxx = max(tree[i<<1].maxx, tree[i<<1|1].maxx);
}
ll query(int i, int l, int r){
if(tree[i].l == l && tree[i].r == r)
return tree[i].sum;
int mid = (tree[i].l+tree[i].r)>>1;
if(r <= mid) return query(i<<1, l, r);
else if(l > mid) return query(i<<1|1, l, r);
else return query(i<<1, l, mid) + query(i<<1|1, mid+1, r);
}
int main()
{
int n, m, cas = 1;
while(sd(n) != EOF){
printf("Case #%d:\n", cas++);
rep(i, 1, n+1)
sld(num[i]);
build(1, 1, n);
sd(m);
rep(i, 0, m){
int op, l, r;
sddd(op, l, r);
if(l > r) swap(l, r);
if(op == 0) update(1, l, r);
else if(op == 1) pld(query(1, l, r));
}
}
return 0;
}