[SPOJ1043,1557,1716,2713,2916,4487,6779,19543]GSS八题系列

[SPOJ1043]GSS1

求区间最大字段和,不带修改,线段树维护总和sum,最大子段和gss,最大左子段和lgss,最大右子段和rgss,即可。
更新答案:

        sum=ls->sum+rs->sum;
        lgss=max(ls->lgss,ls->sum+rs->lgss);
        rgss=max(rs->rgss,rs->sum+ls->rgss);
        gss=max(max(ls->gss,rs->gss),ls->rgss+rs->lgss);

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,a[50010];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node
{
    int lgss,rgss,gss,sum;
};

struct tree
{
    int l,r,lgss,rgss,gss,sum;
    tree *ls,*rs;
    tree()
    {
        ls=rs=NULL;
        l=r=lgss=rgss=gss=sum=0;
    }
    void update()
    {
        sum=ls->sum+rs->sum;
        lgss=max(ls->lgss,ls->sum+rs->lgss);
        rgss=max(rs->rgss,rs->sum+ls->rgss);
        gss=max(max(ls->gss,rs->gss),ls->rgss+rs->lgss);
    }
    void build(int lx,int rx)
    {
        l=lx;r=rx;
        if(l==r) {lgss=rgss=gss=sum=a[l]; return;}
        int mid=(l+r)>>1;
        (ls=new tree)->build(lx,mid);
        (rs=new tree)->build(mid+1,rx);
        update(); 
    }
    node query(int lx,int rx)
    {
        node re;
        if(lx==l&&rx==r) 
        {
            re.lgss=lgss;
            re.rgss=rgss;
            re.gss=gss;
            re.sum=sum;
            return re;
        }
        int mid=(l+r)>>1;
        if(rx<=mid) return ls->query(lx,rx);
        else if(lx>mid) return rs->query(lx,rx);
        else
        {
            node la=ls->query(lx,mid),ra=rs->query(mid+1,rx);
            re.sum=la.sum+ra.sum;
            re.lgss=max(la.lgss,la.sum+ra.lgss);
            re.rgss=max(ra.rgss,ra.sum+la.rgss);
            re.gss=max(max(la.gss,ra.gss),la.rgss+ra.lgss);
            return re;
        }

    }
}*xtr;
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    (xtr=new tree)->build(1,n); 
    m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        printf("%d\n",xtr->query(x,y).gss);
    }
    return 0;
}

[spoj1557]GSS2

求最大子段和,重复的数字只能算一次。
这种重复只算一次的题目的思想就是离线给询问排序,预处理a[i]上一次出现的位置pre[i],把一个区间压缩到一个点,转化成区间加操作。
这题就是先把询问按右端点排序。对序列从左到右操作,操作到第i个点时,保证线段树前i个点(x)的值为去重后的∑(a[j]){x<=j<=i},并解决右端点为i的询问,其实我们只要给[pre[i]+1,i]区间加上i就可以维护。那么这题就转化为线段树区间加和求历史最值。
维护max,oldmax,add,oldadd分别表示当前最大值,历史最大值,当前加法标记,历史最大加法标记。
下发标记:

        if(add!=0||oadd!=0)
        {
            chkmax(ls->oadd,ls->add+oadd);
            chkmax(ls->omx,ls->mx+oadd);
            ls->mx+=add; ls->add+=add;
            chkmax(rs->oadd,rs->add+oadd);
            chkmax(rs->omx,rs->mx+oadd);
            rs->mx+=add; rs->add+=add;
            add=oadd=0;
        }

至于这一句话chkmax(ls->omx,ls->mx+oadd)为什么是用当前最大值+历史最大标记来更新历史最大值,因为历史最大值的意义表示加法的操作做了某一个前缀(因为后面的值<0而变小于是舍弃)的结果,历史最大标记的意义也是表示从上一次标记清零开始最大的加法操作前缀,如果要更新历史最大值,就要求加法操作一定要做完至上一次标记清零(也就是还未更新当前最大值),在加上这个最大前缀,才能保证这样做后还是一个前缀。
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
using namespace std;
const int maxn=100010;
int n,m,a[maxn],pre[maxn],visit[maxn<<1];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node
{
    int x,y,id;
    ll ans;
}q[maxn];
bool cmp1(node a,node b){return a.y<b.y;}
bool cmp2(node a,node b){return a.id<b.id;}
struct tree
{
    int l,r;
    ll omx,mx,add,oadd;
    tree *ls,*rs;
    tree()
    {
        ls=rs=NULL;
        l=r=omx=mx=add=oadd=0;
    }
    void cal(ll c)
    {
        add+=c;
        mx+=c;
        chkmax(omx,mx);
        chkmax(oadd,add);
    }
    void pushdown()
    {
        if(add!=0||oadd!=0)
        {
            chkmax(ls->oadd,ls->add+oadd);
            chkmax(ls->omx,ls->mx+oadd);
            ls->mx+=add; ls->add+=add;
            chkmax(rs->oadd,rs->add+oadd);
            chkmax(rs->omx,rs->mx+oadd);
            rs->mx+=add; rs->add+=add;
            add=oadd=0;
        }
    }
    void update()
    {
        mx=max(ls->mx,rs->mx);
        omx=max(ls->omx,rs->omx);       
    }
    void build(int lx,int rx)
    {
        l=lx;r=rx;
        if(l==r) {omx=mx=0; return;}
        int mid=(l+r)>>1;
        (ls=new tree)->build(lx,mid);
        (rs=new tree)->build(mid+1,rx);
        update(); 
    }
    void modify(int lx,int rx,ll c)
    {
        if(lx==l&&rx==r) {cal(c); return;}
        pushdown();
        int mid=(l+r)>>1;
        if(rx<=mid) ls->modify(lx,rx,c);
        else if(lx>mid) rs->modify(lx,rx,c);
        else {ls->modify(lx,mid,c);rs->modify(mid+1,rx,c);}
        update();
    }
    ll query(int lx,int rx)
    {
        if(lx==l&&rx==r) return omx;
        pushdown(); 
        int mid=(l+r)>>1;
        if(rx<=mid) return ls->query(lx,rx);
        else if(lx>mid) return rs->query(lx,rx);
        else return max(ls->query(lx,mid),rs->query(mid+1,rx));
        update();
    }
}*xtr;
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    (xtr=new tree)->build(1,n); 
    m=read();
    for(int i=1;i<=m;i++)
    {
        q[i].x=read(),q[i].y=read();q[i].id=i;
    }
    memset(visit,0,sizeof(visit));
    memset(pre,0,sizeof(pre));
    for(int i=1;i<=n;i++)
    {
        pre[i]=visit[a[i]+maxn];
        visit[a[i]+maxn]=i;
    }
    sort(q+1,q+m+1,cmp1);
    int pnt=1;
    for(int i=1;i<=n;i++)
    {
        xtr->modify(pre[i]+1,i,a[i]);
        while(pnt<=m&&q[pnt].y==i) {q[pnt].ans=xtr->query(q[pnt].x,i);pnt++;}
    }
    sort(q+1,q+m+1,cmp2);
    for(int i=1;i<=m;i++)
        printf("%lld\n",q[i].ans);
    return 0;
}

[SPOJ1716]GSS3

带单点修改的最大子段和,类似GSS1。
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,a[50010];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node
{
    int lgss,rgss,gss,sum;
};

struct tree
{
    int l,r,lgss,rgss,gss,sum;
    tree *ls,*rs;
    tree()
    {
        ls=rs=NULL;
        l=r=lgss=rgss=gss=sum=0;
    }
    void update()
    {
        sum=ls->sum+rs->sum;
        lgss=max(ls->lgss,ls->sum+rs->lgss);
        rgss=max(rs->rgss,rs->sum+ls->rgss);
        gss=max(max(ls->gss,rs->gss),ls->rgss+rs->lgss);
    }
    void build(int lx,int rx)
    {
        l=lx;r=rx;
        if(l==r) {lgss=rgss=gss=sum=a[l]; return;}
        int mid=(l+r)>>1;
        (ls=new tree)->build(lx,mid);
        (rs=new tree)->build(mid+1,rx);
        update(); 
    }
    void modify(int x,int c)
    {
        if(l==r) {lgss=rgss=gss=sum=c; return;}
        int mid=(l+r)>>1;
        if(x<=mid) ls->modify(x,c);
        else rs->modify(x,c);
        update(); 
    }
    node query(int lx,int rx)
    {
        node re;
        if(lx==l&&rx==r) 
        {
            re.lgss=lgss;
            re.rgss=rgss;
            re.gss=gss;
            re.sum=sum;
            return re;
        }
        int mid=(l+r)>>1;
        if(rx<=mid) return ls->query(lx,rx);
        else if(lx>mid) return rs->query(lx,rx);
        else
        {
            node la=ls->query(lx,mid),ra=rs->query(mid+1,rx);
            re.sum=la.sum+ra.sum;
            re.lgss=max(la.lgss,la.sum+ra.lgss);
            re.rgss=max(ra.rgss,ra.sum+la.rgss);
            re.gss=max(max(la.gss,ra.gss),la.rgss+ra.lgss);
            return re;
        }

    }
}*xtr;
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    (xtr=new tree)->build(1,n); 
    m=read();
    for(int i=1;i<=m;i++)
    {
        int opt=read(),x=read(),y=read();
        if(opt==0) xtr->modify(x,y);
        else printf("%d\n",xtr->query(x,y).gss);
    }
    return 0;
}

[SPOJ2713]GSS4
区间开方,势能分析线段树,同BZOJ3211,代码都是搬过来的。
代码:

type
  tree=^treenode;
  treenode=record
    l,r:longint;
    max,min:int64;
    sum:int64;
    ls,rs:tree;
  end;

var
  n,i,q,opt,l,r,t,ca:longint;
  delta:array[0..100100]of int64;
  xtr:tree;
function max(x,y:int64):int64;
begin
  if x<y then exit(y)
  else exit(x);
end;
function min(x,y:int64):int64;
begin
  if x>y then exit(y)
  else exit(x);
end;
procedure updata(x:tree);
begin
  x^.max:=max(x^.ls^.max,x^.rs^.max);
  x^.min:=min(x^.ls^.min,x^.rs^.min);
  x^.sum:=x^.ls^.sum+x^.rs^.sum;
end;

procedure build(x:tree;l,r:longint);
var
  mid:longint;
begin
  x^.l:=l;
  x^.r:=r;
  if l=r then
  begin
    x^.max:=delta[l];
    x^.min:=delta[l];
    x^.sum:=delta[l];
    x^.ls:=nil;
    x^.rs:=nil;
    exit;
  end;
  mid:=(l+r)div 2;
  new(x^.ls);new(x^.rs);
  build(x^.ls,l,mid);
  build(x^.rs,mid+1,r);
  updata(x);
end;
function ask(x:tree;l,r:longint):int64;
var
  mid:longint;
begin
  if (x^.l=l)and(x^.r=r) then exit(x^.sum);
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then exit(ask(x^.ls,l,r));
  if l>mid then exit(ask(x^.rs,l,r));
  exit(ask(x^.ls,l,mid)+ask(x^.rs,mid+1,r));
end;
procedure change(x:tree;l,r:longint);
var
  mid:longint;
begin
  if (x^.max<=1)and(x^.min>=0) then exit;
  if x^.l=x^.r then begin x^.sum:=trunc(sqrt(x^.sum)); x^.max:=x^.sum; x^.min:=x^.sum; exit; end;
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then change(x^.ls,l,r)
  else if l>mid then change(x^.rs,l,r)
    else
    begin
      change(x^.ls,l,mid);
      change(x^.rs,mid+1,r);
    end;
  updata(x);
end;
begin
  ca:=0;
  while(not eof) do
  begin
  inc(ca);
  writeln('Case #',ca,':');
  readln(n);
  for i:=1 to n do
    read(delta[i]);
  new(xtr);
  build(xtr,1,n);
  readln(q);
  for i:=1 to q do
  begin
    readln(opt,l,r);
    if l>r then begin t:=l;l:=r;r:=t; end;
    if opt=1 then writeln(ask(xtr,l,r))
    else change(xtr,l,r);
  end;
  writeln;
  end;
end.

[spoj2916]GSS5

分类讨论:

            if(y1<x2)printf("%d\n",xtr->query(y1+1,x2-1).sum+xtr->query(x1,y1).rgss+xtr->query(x2,y2).lgss);
            else 
            {
                int l1,r1,l2,r2,s;
                r1=xtr->query(x1,x2-1).rgss;
                r2=xtr->query(x1,y1).rgss;
                l1=xtr->query(y1+1,y2).lgss;
                l2=xtr->query(x2,y2).lgss;
                s=xtr->query(x2,y1).gss;
                printf("%d\n",max(s,max(r1+l2,r2+l1)));
            }

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,a[50010];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node
{
    int lgss,rgss,gss,sum;
    node() {lgss=rgss=gss=sum=0;}
};

struct tree
{
    int l,r,lgss,rgss,gss,sum;
    tree *ls,*rs;
    tree()
    {
        ls=rs=NULL;
        l=r=lgss=rgss=gss=sum=0;
    }
    void update()
    {
        sum=ls->sum+rs->sum;
        lgss=max(ls->lgss,ls->sum+rs->lgss);
        rgss=max(rs->rgss,rs->sum+ls->rgss);
        gss=max(max(ls->gss,rs->gss),ls->rgss+rs->lgss);
    }
    void build(int lx,int rx)
    {
        l=lx;r=rx;
        if(l==r) {lgss=rgss=gss=sum=a[l]; return;}
        int mid=(l+r)>>1;
        (ls=new tree)->build(lx,mid);
        (rs=new tree)->build(mid+1,rx);
        update(); 
    }
    node query(int lx,int rx)
    {
        node re;
        if(lx>rx) return re;
        if(lx==l&&rx==r) 
        {
            re.lgss=lgss;
            re.rgss=rgss;
            re.gss=gss;
            re.sum=sum;
            return re;
        }
        int mid=(l+r)>>1;
        if(rx<=mid) return ls->query(lx,rx);
        else if(lx>mid) return rs->query(lx,rx);
        else
        {
            node la=ls->query(lx,mid),ra=rs->query(mid+1,rx);
            re.sum=la.sum+ra.sum;
            re.lgss=max(la.lgss,la.sum+ra.lgss);
            re.rgss=max(ra.rgss,ra.sum+la.rgss);
            re.gss=max(max(la.gss,ra.gss),la.rgss+ra.lgss);
            return re;
        }

    }
}*xtr;
int main()
{
    int ca=read();
    while(ca--)
    {
        n=read();
        for(int i=1;i<=n;i++)
        a[i]=read();
        (xtr=new tree)->build(1,n); 
        m=read();
        for(int i=1;i<=m;i++)
        {
            int x1=read(),y1=read(),x2=read(),y2=read();
            if(y1<x2)printf("%d\n",xtr->query(y1+1,x2-1).sum+xtr->query(x1,y1).rgss+xtr->query(x2,y2).lgss);
            else 
            {
                int l1,r1,l2,r2,s;
                r1=xtr->query(x1,x2-1).rgss;
                r2=xtr->query(x1,y1).rgss;
                l1=xtr->query(y1+1,y2).lgss;
                l2=xtr->query(x2,y2).lgss;
                s=xtr->query(x2,y1).gss;
                printf("%d\n",max(s,max(r1+l2,r2+l1)));
            }
        }
    }
    return 0;
}

[SPOJ4487]

带插入,删除,修改的最大子段和,用splay即可。
读操作符的时候不要用getchar(),卡了两个多小时。。。
因为不同于线段树,splay上的一个点也表示序列的一个位置(子树表示一个区间),所以更新答案有所不同:

        sum=l->sum+r->sum+t;
        num=l->num+r->num+1;
        lgss=max(l->lgss,l->sum+t+max(0,r->lgss));
        rgss=max(r->rgss,r->sum+t+max(0,l->rgss));
        gss=max(max(l->gss,r->gss),max(0,l->rgss)+t+max(0,r->lgss));

优化时间复杂度:O(n)建树(为了方便序列左右端再建两个没用的点,避免加点的时候找不到前驱或者后继),replace不要先delete再insert。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=200010;
int n,q,a[maxn];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct tree;
tree *NUL;
struct tree
{
    int t,lgss,rgss,gss,sum,num;
    tree *l,*r,*f;
    tree()
    {
        l=r=f=NUL;
        lgss=rgss=gss=t=-1e9;
        sum=num=0;
    }
    void update()
    {
        sum=l->sum+r->sum+t;
        num=l->num+r->num+1;
        lgss=max(l->lgss,l->sum+t+max(0,r->lgss));
        rgss=max(r->rgss,r->sum+t+max(0,l->rgss));
        gss=max(max(l->gss,r->gss),max(0,l->rgss)+t+max(0,r->lgss));
    }

    void left()
    {
        f->r=l;
        if(l!=NUL) l->f=f;
        l=f;f=f->f;l->f=this;
        if(f!=NUL)
        {
            if(f->l==l)  f->l=this;
            else f->r=this;
        }
        l->update();
        update();
    }
    void right()
    {
        f->l=r;
        if(r!=NUL) r->f=f;
        r=f;f=f->f;r->f=this;
        if(f!=NUL)
        {
            if(f->l==r)  f->l=this;
            else f->r=this;
        }
        r->update();
        update();
    }
    void splay(tree *&s,tree *top)
    {
        while(f!=top)
        {
            if(f->f==top)
            {
                if(f->l==this) right();
                else left();
            }
            else
            {
                if(f->f->l==f) 
                {
                    if(f->l==this) f->right();
                    else left();
                    right();
                }   
                else 
                {
                    if(f->r==this) f->left();
                    else right();
                    left();
                }
            }
        }
        if(top==NUL) s=this;
    }
    void init(int c)
    {
        gss=lgss=rgss=sum=t=c;
        num=1;
    }
    void build(int lx,int rx)
    {
        int mid=(lx+rx)>>1;
        init(a[mid]);
        if(lx<mid){(l=new tree)->build(lx,mid-1);l->f=this;}
        if(rx>mid){(r=new tree)->build(mid+1,rx);r->f=this;}
        update();
    }
}*str;
tree *find(int k,tree *p)
{
    int tmp=p->l->num+1;
    if(k==tmp) return p;
    if(k>tmp) return find(k-tmp,p->r);
    else return find(k,p->l);
}

void ins(int pl,int c,tree *&s)
{
    tree *v=new tree;
    find(pl-1,s)->splay(s,NUL);
    find(pl,s)->splay(s,s);
    v->init(c);s->r->l=v;v->f=s->r;
    v->splay(s,NUL);
}
void del(int pl,tree *&s)
{
    find(pl,s)->splay(s,NUL);
    find(pl-1,s)->splay(s,s);
    s->r->f=s->l; s->l->r=s->r; s->l->f=NUL; s=s->l;
}
void rpl(int pl,int c,tree *&s)
{
    tree *p=find(pl,s);
    p->init(c);
    p->splay(s,NUL);
}
int query(int lx,int rx,tree *&s)
{
    find(lx-1,s)->splay(s,NUL);
    find(rx+1,s)->splay(s,s);
    return s->r->l->gss;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    NUL=new tree;   
    (str=new tree)->build(0,n+1);
    q=read();
    for(int i=1;i<=q;i++)
    {
        char opt;
        scanf("%s",&opt);
        int x=read(),y;
        if(opt=='I') {y=read();ins(x+1,y,str);}
        if(opt=='D') {del(x+1,str);}
        if(opt=='R') {y=read();rpl(x+1,y,str);}
        if(opt=='Q') {y=read();printf("%d\n",query(x+1,y+1,str));}
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值