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\n",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\n",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\n",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:\n",++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\n",query(1,1,n,x,y)); } printf("\n"); } 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\n",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\n",ans); } } } return 0; }
(待更)