解析:
这里的并查集并不能用集合合并的思想来考虑,
这里并查集有点像图论中的 DAG D A G 里的边,记录的是当前节点的下一个(包括当前)非1节点的位置。
显然,对1开方对答案是没有任何影响的。
那么我们就只对非1的开方。
那么我们就只遍历非1的节点。
修改和查询用树状数组就好了。
一个数最多被开方
log log arri
l
o
g
l
o
g
a
r
r
i
次,所以复杂度是均摊的,套上树状数组的
O(logn)
O
(
l
o
g
n
)
也能过。
不知道那些说并查集复杂度有问题的人是什么心态。。。这里明明是均摊的复杂度。。。
线段树做法也很简单啊,记录这个区间还是否需要修改,不用修改就直接 return r e t u r n ,仍然是均摊复杂度。
代码(树状数组+并查集):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
ll getint(){
re ll num=0;
re char c;
while(!isdigit(c=gc()));
while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
return num;
}
inline
void outint(ll a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=(a-a/10*10)^48,a/=10;
while(ch[0])pc(ch[ch[0]--]);
}
inline
int lowbit(int k){return k&(-k);}
int n,m;
ll a[100002];
inline
void add(int pos,ll d){
while(pos<=n){
a[pos]+=d;
pos+=lowbit(pos);
}
}
inline
ll query(int pos){
ll res=0;
while(pos){
res+=a[pos];
pos-=lowbit(pos);
}
return res;
}
int fa[100002];
int getfa(int x){
return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
ll arr[100002];
int main(){
n=getint();
for(int re i=1;i<=n;++i){
arr[i]=getint();
add(i,arr[i]);
fa[i]=i;
}
fa[n+1]=n+1;//哨兵节点
m=getint();
while(m--){
int op=getint();
int l=getint(),r=getint();
if(l>r)swap(l,r);
switch(op){
case 1:{
outint(query(r)-query(l-1));pc('\n');
break;
}
case 0:{
for(int re i=l;i<=r;i=getfa(i+1)){
ll sq=sqrt(arr[i]);
add(i,sq-arr[i]);
arr[i]=sq;
if(arr[i]==1)fa[i]=i+1;
}
break;
}
}
}
return 0;
}
代码(线段树):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
ll getint(){
re ll num=0;
re char c;
while(!isdigit(c=gc()));
while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
return num;
}
inline
void outint(ll a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=(a-a/10*10)^48,a/=10;
while(ch[0])pc(ch[ch[0]--]);
}
ll sum[400005];
int n,m;
ll a[100002];
bool sam1[400005];
inline
void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
sam1[k]=sam1[k<<1]&&sam1[k<<1|1];
}
inline
void build(int k,int l,int r){
if(l==r){
sum[k]=a[l];
sam1[k]=(a[l]==1);
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
inline
void modify(int k,int l,int r,cs int &ql,cs int &qr){
if(sam1[k])return;
if(l==r){
sum[k]=floor(sqrt(sum[k]));
sam1[k]=(sum[k]==1);
return ;
}
int mid=(l+r)>>1;
if(ql<=mid)modify(k<<1,l,mid,ql,qr);
if(qr>mid)modify(k<<1|1,mid+1,r,ql,qr);
pushup(k);
}
inline
ll query(int k,int l,int r,cs int &ql,cs int &qr){
if(ql<=l&&r<=qr)return sum[k];
int mid=(l+r)>>1;
if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
if(mid>=qr)return query(k<<1,l,mid,ql,qr);
return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
}
signed main(){
n=getint();
for(int re i=1;i<=n;++i)a[i]=getint();
build(1,1,n);
m=getint();
while(m--){
int op=getint();
int l=getint(),r=getint();
if(l>r)swap(l,r);
switch(op){
case 1:{
outint(query(1,1,n,l,r));pc('\n');
break;
}
case 0:{
modify(1,1,n,l,r);
break;
}
}
}
return 0;
}