题目大意: 一个序列,两种操作,区间开方,区间求和。
线段树裸题,区间求和显然容易搞,区间开方暴力做到叶子就可以了。
因为最大的数才 1 0 12 10^{12} 1012,所以最多开6次方就会变成1,我们只需要判一下这个区间内是否都是1,如果是,就不继续往下做了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define ll long long
int n,m;
ll a[100010];
struct node{
int l,r;
ll z;//记录区间和
node *zuo,*you;
node(int x,int y)
{
l=x,r=y;
if(l<r)
{
int mid=l+r>>1;
zuo=new node(l,mid);
you=new node(mid+1,r);
z=zuo->z+you->z;
}
else z=a[l];
}
void change(int x,int y)
{
if(z==(ll)r-l+1)return;//如果全是1,就不往下做了
if(l==r){z=(ll)sqrt(z);return;}//到了叶子节点,就开个方
int mid=l+r>>1;
if(y<=mid)zuo->change(x,y);
else if(x>=mid+1)you->change(x,y);
else zuo->change(x,mid),you->change(mid+1,y);
z=zuo->z+you->z;//记得修改非叶子节点的z
}
ll getsum(int x,int y)//线段树标准求和
{
if(l==x&&r==y)return z;
int mid=l+r>>1;
if(y<=mid)return zuo->getsum(x,y);
else if(x>=mid+1)return you->getsum(x,y);
else return zuo->getsum(x,mid)+you->getsum(mid+1,y);
}
};
node *root;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
root=new node(1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int id,x,y;
scanf("%d %d %d",&id,&x,&y);
if(x>y)swap(x,y);
if(id==0)root->change(x,y);
else printf("%lld\n",root->getsum(x,y));
}
}