题目简要:我们需要一个能支持区间内每一个数开方以及区间求和的数据结构。
解题思路:说道区间修改区间查询,第一个想到的当然就是分块线段树。数据范围要用long long。本来我是看到区间这两个字就想着运用一下还不算特别熟的lazy-tag。但是题目是开方嘛。开方不满足结合律,√4+√4≠√8是很显而易见的事情。所以说是不能直接修改sum的。那么只能每个单点修改。
如何单点修改?第一个思考的是for循环一下然后套单点。但是实际上这样时间复杂度会极度的退化,是会超时的。此时我们发现每次单点change的时候会重复的访问包含目标节点的大区间再到最小的叶节点。实际上修改a[i]与a[i+1]就是左右的兄弟节点并且还有共同的父亲。所以我们还是用区间修改的模式。只不过要到叶节点才改。
我们按照上面写的做了以后莫约能拿到四十分左右。那么如何优化呢?我们知道260大概就是长整型的极限了。也就是说一个数最多计算60次的平方根就会到达1。并且再开方也就不会变化。换而言之如果一个区间里面全是1,即sum为区间长度,就可以不用处理了。这里题目有说都为正整数,就不用考虑会有0而不好求是否全为1的情况了。
#include<iostream> #include<cstdio> #include<cmath> #define cm int mid=(l+r)>>1 #define zc k<<1 #define yc (k<<1)+1 #define din l>=z&&r<=y #define dout r<z||l>y using namespace std; long long read(){ char ch; long long res=0,f=1; ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ res=res*10+(ch-'0'); ch=getchar(); } return res*f; } long long n,m,a[1600005],xds_sum[1600005]; void build(int k,int l,int r){ if(l==r){xds_sum[k]=a[l];return;} cm;build(zc,l,mid);build(yc,mid+1,r); xds_sum[k]=xds_sum[zc]+xds_sum[yc]; } void change(int k,int l,int r,int z,int y){ if(dout)return; if(din&&xds_sum[k]==(r-l+1))return; if(din&&l==r){xds_sum[k]=(long long)sqrt(xds_sum[k]);return;} cm;change(zc,l,mid,z,y);change(yc,mid+1,r,z,y); xds_sum[k]=xds_sum[zc]+xds_sum[yc]; } long long query(int k,int l,int r,int z,int y){ if(dout)return 0; if(din)return xds_sum[k]; cm;return query(zc,l,mid,z,y)+query(yc,mid+1,r,z,y); } int main(){ n=read(); for(int i=1;i<=n;++i)a[i]=read(); build(1,1,n); m=read(); for(int i=1;i<=m;++i){ int order,x,y; order=read();x=read();y=read(); if(x>y)swap(x,y); if(order)printf("%lld ",query(1,1,n,x,y)); else change(1,1,n,x,y); } return 0; }