线段树中等题
题意:
n个数,两种操作:0 b c表示将[b,c]区间的数开方,1 b c表示询问[b,c]区间的和。
开始总是超时然后借鉴了别人的博客,有两个坑点一个是输出,还是一个是b可能大于c,这时要把他俩交换一下。还有本题主要思路是能想到long long 范围的数开根号最多不超过7次。
具体看代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;
long long a[100005*4];
bool flag[100005*4]; //标记当前区间是否还需要开根号
int n;
void uprt(int k)
{
flag[k]=(flag[k<<1]||flag[k<<1|1]); //如果两个字区间任何一个需要开根号,则把大区间也标记
a[k]=a[k<<1]+a[k<<1|1];
}
void built(int l,int r,int k)
{
if(l==r)
{
scanf("%lld",&a[k]);
flag[k]=1;
if(a[k]<=1) //如果此区间下的值小于等于1,则以后都不需要开根号,因为根号1等于1
flag[k]=0;
return ;
}
int mid=(l+r)/2;
built(l,mid,k<<1);
built(mid+1,r,k<<1|1);
uprt(k);
}
long long search(int l,int r,int ll,int rr,int k) //查询操作,和往常一样
{
if(l==ll&&r==rr)
{
return a[k];
}
int mid=(l+r)/2;
if(mid>=rr)
return search(l,mid,ll,rr,k<<1);
else if(mid<ll)
return search(mid+1,r,ll,rr,k<<1|1);
else
return search(l,mid,ll,mid,k<<1)+search(mid+1,r,mid+1,rr,k<<1|1);
}
void add(int l,int r,int ll,int rr,int k) //更新
{
if(ll==l&&r==rr)
{
if(!flag[k]) //如果当前区间不需要更新则返回
return ;
}
if(l==r) //更新到叶子结点
{
a[k]=floor(sqrt(a[k]));
if(a[k]<=1)
flag[k]=0;
return ;
}
int mid=(l+r)/2;
if(mid>=rr)
{
add(l,mid,ll,rr,k<<1);
}
else if(mid<ll)
{
add(mid+1,r,ll,rr,k<<1|1);
}
else
{
add(l,mid,ll,mid,k<<1);add(mid+1,r,mid+1,rr,k<<1|1);
}
uprt(k);
}
int main()
{
int cas=1;
while(scanf("%d",&n)!=EOF)
{
built(1,n,1);
int m;scanf("%d",&m);
printf("Case #%d:\n",cas++);
while(m--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(y>z)swap(z,y);
if(x==1) //0区间开根号 1区间求和
{
printf("%lld\n",search(1,n,y,z,1));
}
else
{
add(1,n,y,z,1);
}
}
puts("");
}
return 0;
}