使用线段树保存区间和来做
首先因为更新是对区间内每个数字进行开方,所以不能区间更新了,每次需要计算全部的数
之后发现在数据范围内的数,差不多开方6次左右就会变成1,之后就不会再变
想到可以做一个标记,如果此区间下所有数字都为1,就为true,否则为false
这样碰到标记为true的就不用继续更新了
有个坑点,不一定X<=Y,要自己调换顺序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=100000+10; //数组大小
ll sum[maxn<<2];
bool vis[maxn<<2];
ll value[maxn]; //初始值
void PushUp(int rt) //向上更新父节点
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
vis[rt]=vis[rt<<1]&&vis[rt<<1|1];
}
void build(int l,int r,int rt) //建树
{
if(l==r)
{
vis[rt]=false;
sum[rt]=value[l];
if(sum[rt]==1) vis[rt]=true;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
PushUp(rt);
}
void update(int L,int R,int l,int r,int rt) //将L~R区间加上c,可以根据需要修改更新函数
{
if(vis[rt]) return;
if(l==r)
{
sum[rt]=sqrt(sum[rt]);
if(sum[rt]==1) vis[rt]=true;
return;
}
int m=(l+r)>>1;
if(L<=m) update(L,R,l,m,rt<<1);
if(m<R) update(L,R,m+1,r,rt<<1|1);
PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt) //查询L~R
{
if(L<=l&&R>=r)
return sum[rt];
int m=(l+r)>>1;
ll ret=0;
if(L<=m) ret+=query(L,R,l,m,rt<<1);
if(m<R) ret+=query(L,R,m+1,r,rt<<1|1);
return ret;
}
int n,q;
int a,b,c;
int main()
{
//freopen("/home/zlwang/test.txt","r",stdin);
int kase=0;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
scanf("%lld",&value[i]);
build(1,n,1);
printf("Case #%d:\n",++kase);
scanf("%d",&q);
for(int i=0;i<q;i++)
{
scanf("%d%d%d",&c,&a,&b);
if(b<a)
{
int t=b;
b=a;
a=t;
}
if(c)
printf("%lld\n",query(a,b,1,n,1));
else
update(a,b,1,n,1);
}
printf("\n");
}
return 0;
}