zoukankan      html  css  js  c++  java
  • SPOJ GSS 系列

    来怒做GSS系列了;

    GSS1:https://www.luogu.org/problemnew/show/SP1043

    这题就是维护一个 sum , mx , lmx , rmx,转移时用结构体就好了。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    int const maxn=50005;
    int n,m,a[maxn];
    struct N{
        int sum,mx,lmx,rmx;
    }t[maxn<<2];
    void pushup(int x)
    {
        int ls=(x<<1),rs=(x<<1|1);
        t[x].sum=t[ls].sum+t[rs].sum;
        t[x].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
        t[x].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
        t[x].mx=max(t[x].sum,max(t[x].lmx,t[x].rmx));
        t[x].mx=max(t[x].mx,max(t[ls].mx,t[rs].mx));
        t[x].mx=max(t[x].mx,t[ls].rmx+t[rs].lmx);
    }
    N merge(N l,N r)
    {
        N ret;
        ret.sum=l.sum+r.sum;
        ret.lmx=max(l.lmx,l.sum+r.lmx);
        ret.rmx=max(r.rmx,r.sum+l.rmx);
        ret.mx=max(ret.sum,max(ret.lmx,ret.rmx));
        ret.mx=max(ret.mx,max(l.mx,r.mx));
        ret.mx=max(ret.mx,l.rmx+r.lmx);
        return ret;
    }
    void build(int x,int l,int r)
    {
        if(l==r){t[x].mx=t[x].lmx=t[x].rmx=t[x].sum=a[l]; return;}
        int mid=((l+r)>>1);
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        pushup(x);
    }
    N query(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return t[x];
        int mid=((l+r)>>1);
        if(mid<L)return query(x<<1|1,mid+1,r,L,R);
        else if(mid>=R)return query(x<<1,l,mid,L,R);
        else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n);
        scanf("%d",&m);
        for(int i=1,x,y;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            printf("%d
    ",query(1,1,n,x,y).mx);
        }
        return 0;
    }

    GSS2:https://www.luogu.org/problemnew/show/SP1557

    突然好难,不会做了...

    看了半天TJ,还是云里雾里...可能是对最大连续子段和的知识太不熟悉了;

    大概就是要离线,把询问按右端点排序,然后逐个加点;

    因为要去重,所以新加入一个点,影响到的左端点的区间只在上一个这个值出现的位置之后,于是可以用线段树做;

    线段树叶子节点上的值表示以这个点为左端点、当前节点(i)为右端点的各种值;

    sum 是不断累加的和,his 是历史上的最大值,也是查询的答案;

    因为是线段树,所以要考虑一下标记,区分成不断累加的标记 tag 和历史上的最大增量 htg;

    pushdown 的转移有点复杂,要注意顺序;

    还是感觉好难想(其实是套路?)好难写啊...

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const maxn=1e5+5;
    int n,m,a[maxn],pre[maxn],lst[maxn<<1];
    ll ans[maxn];
    struct N{
        ll sum,his,tag,htg;
        N(){sum=his=tag=htg=0;}
    }t[maxn<<2];
    struct Q{int l,r,bh;}q[maxn];
    bool cmp(Q x,Q y){return x.r<y.r;}
    N merge(N l,N r)
    {
        N ret;
        ret.sum=max(l.sum,r.sum);
        ret.his=max(l.his,r.his);
        return ret;
    }
    void pushup(int x){t[x]=merge(t[x<<1],t[x<<1|1]);}
    void pushdown(int x)
    {
        int ls=(x<<1),rs=(x<<1|1);
        t[ls].his=max(t[ls].his,t[ls].sum+t[x].htg);
        t[rs].his=max(t[rs].his,t[rs].sum+t[x].htg);
        t[ls].sum+=t[x].tag;
        t[rs].sum+=t[x].tag;
        t[ls].htg=max(t[ls].htg,t[ls].tag+t[x].htg);
        t[rs].htg=max(t[rs].htg,t[rs].tag+t[x].htg);
        t[ls].tag+=t[x].tag;
        t[rs].tag+=t[x].tag;
        t[x].tag=0; t[x].htg=0;
    }
    void insert(int x,int l,int r,int L,int R,int val)
    {
        if(l>=L&&r<=R)
        {
            t[x].sum+=val;
            t[x].his=max(t[x].his,t[x].sum);
            t[x].tag+=val;
            t[x].htg=max(t[x].htg,t[x].tag);
            return;
        }
        pushdown(x);
        int mid=((l+r)>>1);
        if(mid>=L)insert(x<<1,l,mid,L,R,val);
        if(mid<R)insert(x<<1|1,mid+1,r,L,R,val);
        pushup(x);
    }
    N query(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return t[x];
        pushdown(x);
        int mid=((l+r)>>1);
        if(mid<L)return query(x<<1|1,mid+1,r,L,R);
        else if(mid>=R)return query(x<<1,l,mid,L,R);
        else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pre[i]=lst[a[i]+maxn];
            lst[a[i]+maxn]=i;
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].bh=i;
        sort(q+1,q+m+1,cmp);
        for(int i=1,j=1;i<=n;i++)
        {
            insert(1,1,n,pre[i]+1,i,a[i]);
            while(q[j].r<=i&&j<=m)ans[q[j].bh]=query(1,1,n,q[j].l,q[j].r).his,j++;
        }
        for(int i=1;i<=m;i++)printf("%lld
    ",ans[i]);
        return 0;
    }

    GSS3:https://www.luogu.org/problemnew/show/SP1716

    GSS1加修改。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    int const maxn=50005;
    int n,m,a[maxn];
    struct N{
        int sum,mx,lmx,rmx;
    }t[maxn<<2];
    void pushup(int x)
    {
        int ls=(x<<1),rs=(x<<1|1);
        t[x].sum=t[ls].sum+t[rs].sum;
        t[x].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
        t[x].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
        t[x].mx=max(t[x].sum,max(t[x].lmx,t[x].rmx));
        t[x].mx=max(t[x].mx,max(t[ls].mx,t[rs].mx));
        t[x].mx=max(t[x].mx,t[ls].rmx+t[rs].lmx);
    }
    N merge(N l,N r)
    {
        N ret;
        ret.sum=l.sum+r.sum;
        ret.lmx=max(l.lmx,l.sum+r.lmx);
        ret.rmx=max(r.rmx,r.sum+l.rmx);
        ret.mx=max(ret.sum,max(ret.lmx,ret.rmx));
        ret.mx=max(ret.mx,max(l.mx,r.mx));
        ret.mx=max(ret.mx,l.rmx+r.lmx);
        return ret;
    }
    void build(int x,int l,int r)
    {
        if(l==r){t[x].mx=t[x].lmx=t[x].rmx=t[x].sum=a[l]; return;}
        int mid=((l+r)>>1);
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        pushup(x);
    }
    N query(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return t[x];
        int mid=((l+r)>>1);
        if(mid<L)return query(x<<1|1,mid+1,r,L,R);
        else if(mid>=R)return query(x<<1,l,mid,L,R);
        else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
    }
    void update(int x,int l,int r,int pos,int val)
    {
        if(l==r){t[x].mx=t[x].lmx=t[x].rmx=t[x].sum=val; return;}
        int mid=((l+r)>>1);
        if(pos<=mid)update(x<<1,l,mid,pos,val);
        else update(x<<1|1,mid+1,r,pos,val);
        pushup(x);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n);
        scanf("%d",&m);
        for(int i=1,op,x,y;i<=m;i++)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==0)update(1,1,n,x,y);
            if(op==1)printf("%d
    ",query(1,1,n,x,y).mx);
        }
        return 0;
    }

    GSS4:https://www.luogu.org/problemnew/show/SP2713

    由于一个数开方几次就到1了,所以对于一个全是1的区间就不用去修改了。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    int const maxn=1e5+5;
    int n,m,u;
    ll a[maxn],sum[maxn<<2];
    void build(int x,int l,int r)
    {
        if(l==r){sum[x]=a[l]; return;}
        int mid=((l+r)>>1);
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        sum[x]=sum[x<<1]+sum[x<<1|1];
    }
    void update(int x,int l,int r,int L,int R)
    {
        if(sum[x]==r-l+1)return;
        if(l==r){sum[x]=sqrt(sum[x]); return;}
        int mid=((l+r)>>1);
        if(L<=mid)update(x<<1,l,mid,L,R);
        if(R>mid) update(x<<1|1,mid+1,r,L,R);
        sum[x]=sum[x<<1]+sum[x<<1|1];
    }
    ll query(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return sum[x];
        int mid=((l+r)>>1); ll ret=0;
        if(mid>=L)ret+=query(x<<1,l,mid,L,R);
        if(mid<R)ret+=query(x<<1|1,mid+1,r,L,R);
        return ret;
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            printf("Case #%d:
    ",++u);
            for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
            build(1,1,n);
            scanf("%d",&m);
            for(int i=1,op,x,y;i<=m;i++)
            {
                scanf("%d%d%d",&op,&x,&y);
                if(x>y)swap(x,y);
                if(op==0)update(1,1,n,x,y);
                if(op==1)printf("%lld
    ",query(1,1,n,x,y));
            }
            printf("
    ");
        }
        return 0;
    }

    GSS5:https://www.luogu.org/problemnew/show/SP2916

    分类讨论,不相交或相交;

    相交分穿过 x2,或穿过 y1,或在 x2 和 y1 中间;

    细节有点神奇,注释那里要注意一下,因为如果是 x1,x2,那么实际还是没有摆脱 x2,所以不行(?);

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    int const maxn=1e5+5,inf=1e9;
    int T,n,m,a[maxn];
    struct N{
        int sum,mx,lmx,rmx;
        N(){sum=mx=lmx=rmx=0;}
    }t[maxn<<2];
    void pushup(int x)
    {
        int ls=(x<<1),rs=(x<<1|1);
        t[x].sum=t[ls].sum+t[rs].sum;
        t[x].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
        t[x].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
        t[x].mx=max(t[ls].rmx+t[rs].lmx,max(t[ls].mx,t[rs].mx));
    }
    N merge(N l,N r)
    {
        N ret;
        ret.sum=l.sum+r.sum;
        ret.lmx=max(l.lmx,l.sum+r.lmx);
        ret.rmx=max(r.rmx,r.sum+l.rmx);
        ret.mx=max(l.rmx+r.lmx,max(l.mx,r.mx));
        return ret;
    }
    void build(int x,int l,int r)
    {
        if(l==r){t[x].sum=t[x].mx=t[x].lmx=t[x].rmx=a[l]; return;}
        int mid=((l+r)>>1);
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        pushup(x);
    }
    N query(int x,int l,int r,int L,int R)
    {
        N ret;
        if(L>R)return ret;
        if(l>=L&&r<=R)return t[x];
        int mid=((l+r)>>1);
        if(mid<L)return query(x<<1|1,mid+1,r,L,R);
        else if(mid>=R)return query(x<<1,l,mid,L,R);
        else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;i++)scanf("%d",&a[i]);
            build(1,1,n);
            scanf("%d",&m);
            for(int i=1,x1,y1,x2,y2;i<=m;i++)
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                if(y1<x2)
                {
                    N ls=query(1,1,n,x1,y1),rs=query(1,1,n,x2,y2),mid=query(1,1,n,y1+1,x2-1);
                    printf("%d
    ",ls.rmx+mid.sum+rs.lmx);
                }
                else
                {
                    int ans=-inf;
                    N ls=query(1,1,n,x1,x2-1),rs=query(1,1,n,x2,y2);//不能是 x1,x2 和 x2+1,y2? 
                    ans=max(ans,ls.rmx+rs.lmx);
                    ls=query(1,1,n,x1,y1),rs=query(1,1,n,y1+1,y2);
                    ans=max(ans,ls.rmx+rs.lmx);
                    ls=query(1,1,n,x2,y1);
                    ans=max(ans,ls.mx);
                    printf("%d
    ",ans);
                }
            }
        }
        return 0;
    }

      (待更)

  • 相关阅读:
    BufferedGraphics 性能测试
    ManualResetEvent 与 AutoResetEvent 区别
    管道式编程(收藏)
    C# 中扩展方法应用
    WinForm Invoke 调用 传入 out 类型参数
    断路器选型的一些理解
    为什么通了PE线,现场设备外壳还需要接地?
    RS485终端电阻解释
    驱动器的“安全转矩关断(Safe Torque Off,STO)”
    TCP和UDP的优缺点及区别
  • 原文地址:https://www.cnblogs.com/Zinn/p/9343106.html
Copyright © 2011-2022 走看看