因为开根号能使数字减小得非常快
所以开不了几次(6次?)很大的数就会变成1.....
所以我们可以维护区间最大值,若最大值>1,则继续递归子树,暴力修改叶节点,否则直接return
(好像也可以维护区间被开方的次数,但我不会。。。QAQ)
#include<cstdio> #include<iostream> #include<cmath> #define int long long #define R register int #define ls (tr<<1) #define rs (tr<<1|1) using namespace std; inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch==‘-‘?-1:fix; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } int n,m; int mx[400010],sum[400010]; inline void build(int tr,int l,int r){ if(l==r) {mx[tr]=sum[tr]=g(); return ;} R md=(l+r)>>1; build(ls,l,md),build(rs,md+1,r); mx[tr]=max(mx[ls],mx[rs]); sum[tr]=sum[ls]+sum[rs]; } inline void calc(int tr,int l,int r,int ll,int rr) { if(mx[tr]<=1) return ; if(l==r) {mx[tr]=sqrt(mx[tr]),sum[tr]=sqrt(sum[tr]); return ;} R md=(l+r)>>1; if(ll>md) calc(rs,md+1,r,ll,rr); else if(rr<md+1) calc(ls,l,md,ll,rr); else calc(ls,l,md,ll,md),calc(rs,md+1,r,md+1,rr); mx[tr]=max(mx[ls],mx[rs]); sum[tr]=sum[ls]+sum[rs]; } inline int query(int tr,int l,int r,int ll,int rr) { if(l==ll&&r==rr) return sum[tr]; R md=(l+r)>>1; if(ll>md) return query(rs,md+1,r,ll,rr); else if(rr<md+1) return query(ls,l,md,ll,rr); else return query(ls,l,md,ll,md)+query(rs,md+1,r,md+1,rr); } signed main() { n=g(); build(1,1,n); m=g(); for(R i=1,k,l,r;i<=m;++i) { k=g(),l=g(),r=g(); if(l>r) swap(l,r); if(k&1) printf("%lld\n",query(1,1,n,l,r)); else calc(1,1,n,l,r); } }
2019.04.11
原文地址:https://www.cnblogs.com/Jackpei/p/10687158.html
时间: 2024-11-09 05:01:37