zoukankan      html  css  js  c++  java
  • Bzoj3211花神游历各国

    提供一种数据结构,支持区间求和,以及区间开根号。

    这种题一般暴力谁都能打,主要是练线段树。

    下面给出两种解法:

    第一种,额外维护区间最大值。

    由于1、0开根是其本身,开根没有意义,我们维护区间最大值,如果区间最大值已经为1或0则直接return,否则继续向下开根。

    剩下的维护区间和就可以了。

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #define ll long long 
    using namespace std;
    struct Sengment_Tree{
        int l,r;
        ll sum,maxn;
    }t[400200];
    int n,m;
    ll read(){
        ll sum=0;int f=1;char x=getchar();
        while(x<'0'||x>'9'){
            if(x=='-') f=-1;
            x=getchar();
        }while(x>='0'&&x<='9'){
            sum=sum*10+x-'0';
            x=getchar();
        }return sum*f;
    }
    void updata(int p){
        t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
        t[p].maxn=max(t[p<<1].maxn,t[p<<1|1].maxn);
    }
    void build(int p,int l,int r){
        t[p].l=l;t[p].r=r;
        if(l==r){
            t[p].sum=t[p].maxn=read();
            return;
        }
        int mid=l+r>>1;
        build(p<<1,l,mid);
        build(p<<1|1,mid+1,r);
        updata(p);
    }
    void change(int p,int l,int r){
        if(t[p].maxn<=1) return ;
        if(t[p].l==t[p].r){
            t[p].sum=t[p].maxn=(ll)sqrt(t[p].sum);
            return ;
        }
        int mid=t[p].l+t[p].r>>1;
        if(l<=mid) change(p<<1,l,r);
        if(r>mid) change(p<<1|1,l,r);
        updata(p);
    }
    ll ask(int p,int l,int r){
        if(l<=t[p].l&&t[p].r<=r) return t[p].sum;
        int mid=t[p].l+t[p].r>>1;
        ll ans=0;
        if(l<=mid) ans+=ask(p<<1,l,r);
        if(r>mid) ans+=ask(p<<1|1,l,r);
        return ans;
    }
    int main(){
        int x,y,z;
        n=read();
        build(1,1,n);
        m=read();
        for(int i=1;i<=m;i++){
            x=read();y=read();z=read();
            if(x==1) printf("%lld
    ",ask(1,y,z));
            else change(1,y,z);
        }return 0;
    }
    View Code

    第二种,维护lazy,表示是否已经全是1或0。

    如果是,则直接return,否则继续向下开根。

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    #define ll long long 
    using namespace std;
    struct Sengment_Tree{
        int l,r,flag;
        ll sum;
    }t[400200];
    ll read(){
        ll sum=0;int f=1;char x=getchar();
        while(x<'0'||x>'9'){
            if(x=='-') f=-1;
            x=getchar();
        }while(x>='0'&&x<='9'){
            sum=sum*10+x-'0';
            x=getchar();
        }return sum*f;
    }
    int n,qnum;
    ll a[100050];
    void updata(int p){
        t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
        t[p].flag=t[p<<1].flag&t[p<<1|1].flag;
    }
    void build(int p,int l,int r){
        t[p].l=l;t[p].r=r;
        if(l==r){
            t[p].sum=a[l];
            if(a[l]==0||a[l]==1)
                t[p].flag=1;
            else t[p].flag=0;
            return ;
        }
        int mid=l+r>>1;
        build(p<<1,l,mid);
        build(p<<1|1,mid+1,r);
        updata(p);
    }
    void change(int p,int l,int r){
        if(t[p].l==t[p].r){
            t[p].sum=(ll)sqrt(t[p].sum);
            if(t[p].sum==0||t[p].sum==1)
                t[p].flag=1;
            return ;
        }
        int mid=t[p].l+t[p].r>>1;
        if(l<=mid&&!t[p<<1].flag) change(p<<1,l,r);
        if(r>mid&&!t[p<<1|1].flag) change(p<<1|1,l,r);
        updata(p);
    }
    ll ask(int p,int l,int r){
    //    cout<<t[p].sum<<endl;
        if(l<=t[p].l&&t[p].r<=r) return t[p].sum;
        int mid=t[p].l+t[p].r>>1;
        ll ans=0;
        if(l<=mid) ans+=ask(p<<1,l,r);
        if(r>mid) ans+=ask(p<<1|1,l,r);
        return ans;
    }
    int main(){
    //    freopen("out.docx","w",stdout);
        int x,y,opt;
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        build(1,1,n);
        qnum=read();
        for(int i=1;i<=qnum;i++){
            opt=read();x=read();y=read();
            if(opt==1) printf("%lld
    ",ask(1,x,y));
            else change(1,x,y);
        }return 0;
    } 
    View Code

    时间复杂度上来说,每个数最多被开大约5次(可以自己拿计算机按按),那么每个区间最多拜访的次数也不会太大,所以均摊一下,过掉这道题是没什么问题的。

  • 相关阅读:
    New Skateboard
    1127
    一张图看懂开源许可协议,开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别
    vim 快捷键绑定
    使用git 上传项目到gitee/github
    Linux进程/线程调度策略与 进程优先级
    【框架】共享内存组设计思路与实现(更新中)
    linux下六大IPC机制【转】
    详解Linux内核红黑树算法的实现
    Linux 内核里的数据结构:红黑树(rb-tree)
  • 原文地址:https://www.cnblogs.com/Yu-shi/p/11138129.html
Copyright © 2011-2022 走看看