题目传送门
首先看标签,好的线段树
有区间的修改,有区间的查询,数据范围
1
≤
n
,
m
≤
1
0
5
1≤n,m≤10^5
1≤n,m≤105,那么就可以考虑用线段树来解。
线段树在之前的文章已经讲过了,这里就不多说,直接进入正题。
这里的区间查询就是普通的一个求和,所以就不用做什么改动。
int query(int p,int l,int r){
if(f[p].r<l||f[p].l>r)return 0;
if(f[p].l>=l&&f[p].r<=r){
return f[p].ans;
}
return query(lc,l,r)+query(rc,l,r);
}
重点在区间修改上。
普通的区查就是加法减法乘法除法,整体的和是有规律可循的。
但是这道题是将每个元素
x
x
x成
x
\sqrt{x}
x,而最后的查询是求和,所以找不到什么式子可以直接求出结果。
那不妨就暴力一点,每一遍查询就从叶子节点重新找上来。
void change(int p,int l,int r){
if(f[p].r<l||f[p].l>r)return;
if(f[p].l==f[p].r){
f[p].ans=sqrt(f[p].ans);
return;
}
change(lc,l,r);
change(rc,l,r);
up(p);
}
哦哦哦, u p up up函数在这里:
void up(int p){
f[p].ans=f[lc].ans+f[rc].ans;
}
然就就TLE了,应为如果每一遍都从叶子结点重新找要更改的点太多了,而且许多点改了都用不上,那该怎么办?
可以从数据范围入手。
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1≤n,m≤105 , 1 ≤ l , r ≤ n 1≤l,r≤n 1≤l,r≤n,数列中的数大于 0 0 0,且不超过 1 0 12 10^{12} 1012
数组中的元素最大就只能到 1 0 12 10^{12} 1012,我们对这个数不断地开更号。
1000000000000
1000000
1000
31
5
2
1
到
1
1
1的时候再开方就会一直是
1
1
1,所以说一个数最多开方
6
6
6次就会变成
1
1
1或者
0
0
0(当这个数是
0
0
0时),再开下去就没有任何意义了。
所以当一个数为
1
1
1或
0
0
0是,我们就再也不用去管他了。同样的,当一个区间里只有
1
1
1和
0
0
0时,这个区间也不用去管他了。
所以我们对刚才的暴力进行优化。
void change(int p,int l,int r){
if(f[p].r<l||f[p].l>r)return;
if(f[p].mx<=1)return;//当区间最大值为小于等于1时,这个区间就只有1或者0了,就不管了
if(f[p].l==f[p].r){
f[p].ans=sqrt(f[p].ans);
f[p].mx=f[p].ans;//更新最大值
return;
}
change(lc,l,r);
change(rc,l,r);
up(p);
}
u p up up函数也改一下
void up(int p){
f[p].ans=f[lc].ans+f[rc].ans;
f[p].mx=max(f[lc].mx,f[rc].mx);
}
最后的代码:
#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=1e6+5;
int n,m;
int a[N];
struct node{
int l,r,ans,mx,add;
}f[4*N];
void up(int p){
f[p].ans=f[lc].ans+f[rc].ans;
f[p].mx=max(f[lc].mx,f[rc].mx);
}
void build(int p,int l,int r){
f[p]={l,r,a[l],a[l],0};
if(l==r)return;
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
up(p);
}
void change(int p,int l,int r){
if(f[p].r<l||f[p].l>r)return;
if(f[p].mx<=1)return;
if(f[p].l==f[p].r){
f[p].ans=sqrt(f[p].ans);
f[p].mx=f[p].ans;
return;
}
change(lc,l,r);
change(rc,l,r);
up(p);
}
int query(int p,int l,int r){
if(f[p].r<l||f[p].l>r)return 0;
if(f[p].l>=l&&f[p].r<=r){
return f[p].ans;
}
return query(lc,l,r)+query(rc,l,r);
}
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
int x,y;
cin>>m;
while(m--){
int op;
cin>>op;
cin>>x>>y;
if(x>y)swap(x,y);//x可能大于y
if(op==0){
change(1,x,y);
}
else{
cout<<query(1,x,y)<<'\n';
}
}
}