1. 开根号操作不具有区间合并性(不像区间加、减、改值),所以此题其实是单点更新,没法用延迟标记。
2. 注意到一个long long范围内的数k(k<=2^63-1)在经过最多6~7次的开方后,必然会变成1,而且1的平方根也是1,也就是说:当一个数为1的时候,我们没有必要对它进行操作和更新,而且一个很大的数仅仅经过6~7次就可以变成1。
3. 由此想出剪枝方法:每当我们要进行更新操作的时候,我们先判断一下这个区间是否有必要进行更新(若全都是1就没有更新的必要了)
判断的方法:看该区间的长度和该区间内的总和是否相等,或用一个标记(不是lazy标记哦)。
当我们确认有必要进行更新的时候,我们就要对整个区间进行更新。
Script1 flag标记
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn=100005;
typedef long long ll;
ll sum[maxn<<2];
bool flag[maxn<<2]; //flag为0 表示此区间的数全为1 这个flag不是lazy标记哦
ll nl,nr;
ll ls(ll p){return p<<1;}
ll rs(ll p){return p<<1|1;}
void pushup(ll p){
sum[p]=sum[ls(p)]+sum[rs(p)];
flag[p]=flag[ls(p)]|flag[rs(p)];
}
void build(ll p, ll l ,ll r){
if(l==r){
scanf("%lld",&sum[p]); //lld
flag[p]=1;
if(sum[p]<=1) flag[p]=0;
return;
}
ll mid=l+r>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
pushup(p);
}
void update(ll p,ll l,ll r){
if(l==r){// if(nl<=l&&r<=nr) -> if(l==r) //单点修改
sum[p]=sqrt(sum[p]);
if(sum[p]<=1) flag[p]=0;
return;
}
// pushdown(p,l,r);//没有区间修改 不用延迟标记
ll mid=l+r>>1;
if(nl<=mid&&flag[ls(p)])update(ls(p),l,mid);// &&ls(p) 是一种剪枝 全为1的区间就不用去开根号了
if(nr>mid&&flag[rs(p)])update(rs(p),mid+1,r);
pushup(p);
}
ll query(ll p,ll l,ll r){
if(nl<=l&&r<=nr){return sum[p];}
ll mid=l+r>>1;
// pushdown(p,l,r);
ll res=0;
if(nl<=mid) res+=query(ls(p),l,mid);
if(nr>mid) res+=query(rs(p),mid+1,r);
return res;
}
int main(){
ll n,m;
int cnt=0;
while(scanf("%lld",&n)!=EOF){
build(1,1,n);
scanf("%lld",&m);
printf("Case #%d:\n",++cnt);
for(int i=1;i<=m;++i){
int k;
scanf("%d%lld%lld",&k,&nl,&nr);
if(nl>nr) swap(nl,nr);//呵呵
if(k){//1:区间和
printf("%lld\n",query(1,1,n));
}else{//0:给区间每个数开平方根取整
update(1,1,n);
}
}
puts("");
}
return 0;
}
Script2 没用标记
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn=100005;
typedef long long ll;
ll sum[maxn<<2];
ll nl,nr;
ll ls(ll p){return p<<1;}
ll rs(ll p){return p<<1|1;}
void pushup(ll p){
sum[p]=sum[ls(p)]+sum[rs(p)];
}
void build(ll p, ll l ,ll r){
if(l==r){
scanf("%lld",&sum[p]);//lld
return;
}
ll mid=l+r>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
pushup(p);
}
void update(ll p,ll l,ll r){
if(l==r){// if(nl<=l&&r<=nr) ->
sum[p]=sqrt(sum[p]);//r-l+1
return;
}
// pushdown(p,l,r);
ll mid=l+r>>1;
if(nl<=mid&&mid-l+1!=sum[ls(p)])update(ls(p),l,mid);// 判断和是否等于长度 是一种剪枝
if(nr>mid&&r-mid!=sum[rs(p)])update(rs(p),mid+1,r);
pushup(p);
}
ll query(ll p,ll l,ll r){
if(nl<=l&&r<=nr){return sum[p];}
ll mid=l+r>>1;
// pushdown(p,l,r);
ll res=0;
if(nl<=mid) res+=query(ls(p),l,mid);
if(nr>mid) res+=query(rs(p),mid+1,r);
return res;
}
int main(){
ll n,m;
int cnt=0;
while(scanf("%lld",&n)!=EOF){
build(1,1,n);
scanf("%lld",&m);
printf("Case #%d:\n",++cnt);
for(int i=1;i<=m;++i){
int k;
scanf("%d%lld%lld",&k,&nl,&nr);
if(nl>nr) swap(nl,nr);
if(k){//1:区间和
printf("%lld\n",query(1,1,n));
}else{//0:给区间每个数开平方根取整
update(1,1,n);
}
}
puts("");
}
return 0;
}
参考博客:https://blog.csdn.net/weiguang_123/article/details/8096210