不需要懒标记,区间修改转为单点修改
思路
已知:0 或 1 开根号后 仍是 0 或 1,又因为 1e9 最多开 5 次根号就会变成 1(每个数最多修改 5 次)
所以本题的区间修改 可以变为单点修改 + 剪枝
为什么不会超时?
对于查询操作,单次查询时间复杂度为 O(logn),最多执行 m 次,所以最坏是 O(m*logn)
对于修改操作,单点修改时间复杂度 O(logn)
又因为有剪枝,每个点最多修改 5 次,且 m 次操作,最多修改 n 个点,所以最坏是 O(5*n*logn)
所以总时间复杂度约为 max(O(m*logn),O(5*n*logn))
flag 取值的含义
flag取值 0 :[l,r]区间里的每个数都不再需要取根号:说明区间里的所有数要么是0,要嘛是1
flag取值 1 :[l,r]区间里存在需要取根号的数
关于 modify 中的剪枝
剪枝:if(tr[u].flag==0) return ; ( 我们令该语句为 (1) )
当语句 (1) 执行时,说明此时所在区间(结点) 里的每个数 ,要嘛为 0 ,要么为 1
即:取根号后值是不变的,所以就不需要对它们取根号了
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int w[N];
int n,m;
struct node{
int l,r;
ll sum;
int flag;
}tr[N*4];
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
tr[u].flag=max(tr[u<<1].flag,tr[u<<1|1].flag);
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r) tr[u].sum=w[l],tr[u].flag=w[l]<=1?0:1;
else{
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r){
if(tr[u].flag==0) return ; // 剪枝
if(tr[u].l==tr[u].r) tr[u].sum=(ll)sqrt(tr[u].sum),tr[u].flag=tr[u].sum<=1?0:1;
else{
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r);
if(r>mid) modify(u<<1|1,l,r);
pushup(u);
}
}
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
int mid=tr[u].l+tr[u].r>>1;
ll sum=0;
if(l<=mid) sum=query(u<<1,l,r);
if(r>mid) sum+=query(u<<1|1,l,r);
return sum;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
scanf("%d",&m);
while(m--){
int x,l,r;
scanf("%d %d %d",&x,&l,&r);
if(x==1) printf("%lld\n",query(1,l,r));
else modify(1,l,r);
}
return 0;
}