题意:将题目的背景去掉,简单的说,就是每一次对一个区间的所有值都分别做一次求平方根的运算,就是将那个值改为它的平方根的值,每次再询问一段区间内的总和
分析:很明显的用线段树来做,不过明显的对线段树还是不熟悉,一开始将每一步update操作都更新到具体的每一个点了,没有任何技巧,直接TLE;很明显,如果每一步都必须更新的具体每一个点的话,就是一个
O(n)复杂度的操作了,这个对使用线段树来说,没太大意义了。
这题目而言,我们发现,任何一个2^63次方以内的数,开根号都至多开八次,也就是,多次询问操作之后,很多都已经不需要update了,所以,只需要在每一个节点中增加一个域,用来标记该区间是否已经全部更新到0或者1了,若满足条件,则不需要更新了
#include<iostream> #include<math.h> #define MAXN 100002 using namespace std; __int64 c[MAXN]; struct node { int l,r; __int64 sum; bool d; }p[MAXN*4]; void bulid(int s,int t,int k) { p[k].l=s;p[k].r=t; if(t==s) { p[k].sum=c[s]; p[k].d=(c[s]<=1LL); return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; bulid(s,mid,kl); bulid(mid+1,t,kr); p[k].sum=p[kl].sum+p[kr].sum; p[k].d=p[kl].d && p[kr].d; } void decr(int k,int l,int r) { if(p[k].l>r||p[k].r<l) return ; if(p[k].d) return; if(p[k].r==p[k].l) { p[k].sum=(__int64)(sqrt((double)p[k].sum)); p[k].d=(p[k].sum<=1LL); return ; } int kl=k<<1,kr=kl+1; decr(kl,l,r); decr(kr,l,r); p[k].sum=p[kr].sum+p[kl].sum; p[k].d=p[kr].d && p[kl].d; } __int64 query(int k,int l,int r) { if(p[k].l>r||p[k].r<l) return 0; if(p[k].l>=l&&p[k].r<=r) return p[k].sum; int kl=k<<1,kr=kl+1; return query(kl,l,r)+query(kr,l,r); } int main() { int n,m,a,b,cas=0,k; while(scanf("%d",&n)==1) { for(int i=1;i<=n;i++) scanf("%I64d",&c[i]); bulid(1,n,1); printf("Case #%d:\n",++cas); scanf("%d",&m); while(m--) { scanf("%d %d %d",&k,&a,&b); if(a>b) {int temp=a;a=b;b=temp;} if(k==0) decr(1,a,b); else printf("%I64d\n",query(1,a,b)); } printf("\n"); } return 0; }