上帝造题的七分钟2 bzoj-3038
题目大意:给定一个序列,支持:区间开方;查询区间和。
注释:$1le nle 10^5$,$1le val[i] le 10^{12}$。
想法:这题还挺挺有意思的。查询区间和我们可以用前缀和,但是用上去区间修改就不难想到线段树。那么我们思考如何在log的时间之内完成区间开方。直接打lazy显然实现不了,其实我们发现,每一个$10^{12}$之内的数最多只需要开6次方就可以变成1,$10^{12}$开6次根号是1.53993。所以我们对每一个区间用一个mark标记表示这个区间是不是全是1。如果不是的话,我就暴力修改。这样的话每一个数最多会被修改6次,所以总时间复杂度是O(n*a),a是log级别的。
P.S.:花神那道题卡读入,不加读入优化会T... ...
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define N 100010 #define lson pos<<1 #define rson pos<<1|1 using namespace std; typedef long long ll; ll sum[N<<2],a[N]; int n,m; inline void pushup(int pos) { sum[pos]=sum[lson]+sum[rson]; } void build(int pos,int l,int r) { if(l==r) { sum[pos]=a[l]; return; } int mid=(l+r)>>1; build(lson,l,mid); build(rson,mid+1,r); pushup(pos); } void update(int pos,int l,int r,int x,int y) { if(x>y)swap(x,y); if(sum[pos]==r-l+1)return; if(l==r) { sum[pos]=(ll)sqrt(sum[pos]+0.5); return; } int mid=(l+r)>>1; if(y<=mid) update(lson,l,mid,x,y); else if(x>mid) update(rson,mid+1,r,x,y); else update(lson,l,mid,x,y),update(rson,mid+1,r,x,y); pushup(pos); } ll getsum(int pos,int l,int r,int x,int y) { if(x>y) swap(x,y); if(x<=l&&r<=y) return sum[pos]; int mid=(l+r)>>1; if(y<=mid)return getsum(lson,l,mid,x,y); if(x>mid)return getsum(rson,mid+1,r,x,y); return getsum(lson,l,mid,x,y)+getsum(rson,mid+1,r,x,y); } void output() { printf("Fuck : %lld ",sum[1]); } int main() { cin >> n ; for(int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); cin >> m ; for(int k,x,y,i=1;i<=m;i++) { scanf("%d%d%d",&k,&x,&y); if(!k) update(1,1,n,x,y); else printf("%lld ",getsum(1,1,n,x,y)); // output(); } return 0; }
小结:线段树是很神奇的...qwq