https://ac.nowcoder.com/acm/contest/877/F
题意:给你一个序列,update:把[l,r]区间的数都开方向下取整;query:求[l,r]区间的gcd。
首先对于一个数开方,最多10次?
gcd可以用线段树维护,还需要维护一个最大值,对于一段全是一的区间(也就是最大值是1),那么就不需要更新了,否则就向下找需要更新的叶子节点。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define mod 1000000007
#define MAXN 2000005
LL a[MAXN],g[MAXN<<2];
LL mx[MAXN<<2];
LL gcd(LL x,LL y){
if(y==0) return x;
else return(gcd(y,x%y));
}
inline void pushUp(int root){
g[root]=gcd(g[root<<1],g[root<<1|1]);
mx[root]=max(mx[root<<1],mx[root<<1|1]);
}
void build(int l,int r,int root){
if(l==r){
g[root]=a[l];mx[root]=a[l];
return;
}
int m=(l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
pushUp(root);
}
void update(int L,int R,int l,int r,int root){
if(l>=L&&r<=R&&mx[root]==1){return;}
if(l==r){
g[root]=(LL)sqrt(g[root]);
mx[root]=(LL)sqrt(mx[root]);;
return;
}
int m=(l+r)>>1;
if(m>=L) update(L,R,l,m,root<<1);
if(m<R) update(L,R,m+1,r,root<<1|1);
pushUp(root);
}
LL query(int L,int R,int l,int r,int root){
if(l>=L&&r<=R){return g[root];}
int m=(l+r)>>1;
if(m>=R) return query(L,R,l,m,root<<1);
if(m<L) return query(L,R,m+1,r,root<<1|1);
return gcd(query(L,R,l,m,root<<1),query(L,R,m+1,r,root<<1|1));
}
int main(){
int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
build(1,n,1);
int q;scanf("%d",&q);while(q--){
int op,ql,qr;scanf("%d%d%d",&op,&ql,&qr);
if(op==0){update(ql,qr,1,n,1);}
if(op==1){
LL ans=query(ql,qr,1,n,1);
printf("%lld\n",ans);
}
}
return 0;
}