[SPOJ1043,1557,1716,2713,2916,4487,6779,19543]GSS八题系列
来源:互联网 发布:paper artist软件下载 编辑:程序博客网 时间:2024/06/16 23:05
[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;}
- [SPOJ1043,1557,1716,2713,2916,4487,6779,19543]GSS八题系列
- SPOJ GSS系列 最大子段和 线段树+树链剖分+splay 1043 1557 1716 2713 2916 4487 6779
- spoj GSS系列
- SPOJ GSS系列
- spoj GSS系列
- 【SPOJ】GSS系列
- spoj GSS系列之GSS1 和 GSS3
- 男人八题系列
- GSS系列(1)——GSS1&&GSS3
- 怒水一记 GSS
- SPOJ GSS
- [BZOJ3211][SPOJ2713][线段树]GSS 4(花神游历各国)[一般题]
- 【安全】GSS-API详解
- SPOJ GSS 1~8
- Datawindow.net 系列八
- svm系列八
- Fitnesse使用系列八
- **MYSQL** 系列八
- 《Effective Java》—— 读后总结
- LINQ体验(16)——LINQ to SQL语句之DataContext
- 用户画像与推荐系统的关系
- 【笔记】滚动数组!
- 2017 Android秋招面试总结
- [SPOJ1043,1557,1716,2713,2916,4487,6779,19543]GSS八题系列
- Banner的基础使用
- 2018秋招前端面经总结
- 【第1083期】git commit 时使用 Emoji
- Java九种基本数据类型的大小
- epoll使用详解(精髓)
- Twitter 是如何做新版手机网站的
- Collecting Bugs POJ 2096
- CentOS x64 里php 源码编译出错参见情况及解决办法