[bzoj3038/3211]上帝造题的七分钟2/花神游历各国_线段树

上帝造题的七分钟2 bzoj-3038

题目大意:给定一个序列,支持:区间开方;查询区间和。

注释:$1\le n\le 10^5$,$1\le 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\n",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\n",getsum(1,1,n,x,y));
		// output();
    }
    return 0;
}

小结:线段树是很神奇的...qwq

posted @ 2018-07-10 20:34  JZYshuraK_彧  阅读(206)  评论(0编辑  收藏  举报