题目链接:bzoj 3211
题意:支持两种操作
- 将区间[L,R]内的数字都开根号
- 查询区间[L,R]内的数字的和
线段树区间更新,只不过这次我们可能需要修改到单点,
对于一个int范围内的非零数字来说最多开六七次就会达到1,故我们可以用线段树维护一个标记数组来表示区间内的数字是不是都为0或者1。
假设我们用bool f[maxn<<2|1]来标记,那么f[k]=(f[k<<1]&f[k<<1|1])。
那么这道题就解决啦,看代码吧。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
ll sum[maxn<<2|1];
bool f[maxn<<2|1];
void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
f[k]=(f[k<<1]&f[k<<1|1]);
}
void build(int l,int r,int k){
if(l==r){
scanf("%lld",&sum[k]);
if(sum[k]<=1) f[k]=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
pushup(k);
}
void updata(int L,int R,int l,int r,int k){
if(l>=L&&r<=R){
if(f[k]) return ;
}
if(l==r){
sum[k]=sqrt(sum[k]);
if(sum[k]<=1) f[k]=1;
return ;
}
int mid=(l+r)>>1;
if(L<=mid) updata(L,R,l,mid,k<<1);
if(R>mid) updata(L,R,mid+1,r,k<<1|1);
pushup(k);
}
ll myfind(int L,int R,int l,int r,int k){
if(l>=L&&r<=R) return sum[k];
int mid=(l+r)>>1;
ll res=0;
if(L<=mid) res+=myfind(L,R,l,mid,k<<1);
if(R>mid) res+=myfind(L,R,mid+1,r,k<<1|1);
return res;
}
int main(){
int n,m;
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
int id,x,y;
while(m--){
scanf("%d%d%d",&id,&x,&y);
if(x>y) swap(x,y);
if(id==1) printf("%lld\n",myfind(x,y,1,n,1));
else if(id==2) updata(x,y,1,n,1);
}
return 0;
}