GSS-Can you answer these queries系列
SPOJ GSS - Can you answer these queries系列
GSS1查询 区间最大子段和
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define ls p<<1
#define rs p<<1|1
inline int read(){
int f=1,x=0; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
return f*x;
}
int min(int x,int y){
return x>y?y:x;
}
int max(int x,int y){
return x>y?x:y;
}
struct TREE{
int lmx,rmx,dat,sum;
}t[N<<2];
int n,m;
inline void pushup(int p){
t[p].sum=t[ls].sum+t[rs].sum;
t[p].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
t[p].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
t[p].dat=max(max(t[ls].dat,t[rs].dat),t[ls].rmx+t[rs].lmx);
}
inline void build(int p,int l,int r){
if(l==r){
t[p].dat=t[p].lmx=t[p].rmx=t[p].sum=read();
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(p);
}
inline TREE query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return t[p];
int mid=(l+r)>>1;
if(ql>mid)return query(rs,mid+1,r,ql,qr);
else if(qr<=mid)return query(ls,l,mid,ql,qr);
else{
TREE ans,a,b;
a=query(ls,l,mid,ql,qr) ; b=query(rs,mid+1,r,ql,qr);
ans.sum=a.sum+b.sum;
ans.lmx=max(a.lmx,a.sum+b.lmx);
ans.rmx=max(b.rmx,b.sum+a.rmx);
ans.dat=max(max(a.dat,b.dat),a.rmx+b.lmx);
return ans;
}
}
int main(){
n=read();
build(1,1,n);
m=read();
while(m--){
int x=read(),y=read();
printf("%d\n",query(1,1,n,x,y).dat);
}
return 0;
}
GSS2求最大子段和,相同的数只算一次
难度飞跃的第二题(虽然问题只多了几个字)
由于题目条件,GSS1的合并lmx,dat,rmx 的方法已不再适用,我们必须另辟蹊径
做题经验告诉我们,要处理有关区间去重的问题时,我们常用到离线算法,本题亦是如此。
仍然是用线段树,只不过我们要把序列中的元素一个个添加进去。
将所有询问按r排序,在最终处理询问到以r为区间右边界时,将a[r]添加到线段树。
线段树每个节点维护4个值,sum,dat,stag,mtag。
假设现在处理到以y为区间右边界的询问了,那么,对于叶结点:
sum表示从这个叶结点所对应的原序列的下标x到y的所有元素和,及a[x]+a[x+1]+a[x+2]+...+a[y],
dat表示sum的历史最大值(最小为0)。
对于内部节点:
sum表示左右儿子的sum的最大值,
dat表示左右儿子的dat的最大值。
另外的,利用stag和mtag作为延迟标记实现单次修改/询问O(logn)复杂度,具体意义参考代码。
如何向线段树添加元素?
记录序列中第i个元素上一个相同元素的位置pre[i],添加元素是[pre[i]+1,i]区间加a[i]即可。
如何查询?
查询[l,r]的dat。
要特别注意pushdown()函数的更新顺序,非常容易弄错。
还有各种实现细节,一定要特别注意。
//求最大子段和,相同的数只算一次
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define INF 1e9+7
#define ls p<<1
#define rs p<<1|1
inline int read(){
int f=1,x=0; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
return f*x;
}
int min(int x,int y){
return x>y?y:x;
}
int max(int x,int y){
return x>y?x:y;
}
struct TREE{
int l,r,dat,sum,stag,mtag;//dat即所求(历史最大)
}t[N<<2];
struct node{
int l,r,id;
}q[N];
bool cmp(node x,node y){
return x.r<y.r;
}
int n,m,a[N],pre[N],last[N*2],ans[N];
inline void build(int p,int l,int r){
t[p].l=l;t[p].r=r;
if(l==r)return;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,1+mid,r);
return;
}
inline void pushup(int p){
t[p].sum=max(t[ls].sum,t[rs].sum);
t[p].dat=max(t[ls].dat,t[rs].dat);
}
inline void pushdown(int p){
t[ls].dat=max(t[ls].dat,t[ls].sum+t[p].mtag);
t[ls].sum+=t[p].stag;
t[ls].mtag=max(t[ls].mtag,t[ls].stag+t[p].mtag);
t[ls].stag+=t[p].stag;
t[rs].dat=max(t[rs].dat,t[rs].sum+t[p].mtag);
t[rs].sum+=t[p].stag;
t[rs].mtag=max(t[rs].mtag,t[rs].stag+t[p].mtag);
t[rs].stag+=t[p].stag;
t[p].mtag=t[p].stag=0;
}
inline void update(int p,int ql,int qr,int x){
if(ql<=t[p].l&&t[p].r<=qr){
t[p].sum+=x;
t[p].dat=max(t[p].sum,t[p].dat);
t[p].stag+=x;
t[p].mtag=max(t[p].mtag,t[p].stag);
return;
}
pushdown(p);
int mid=(t[p].l+t[p].r)>>1;
if(ql<=mid)update(ls,ql,qr,x);
if(qr>mid)update(rs,ql,qr,x);
pushup(p);
}
inline int query(int p,int ql,int qr){
if(ql<=t[p].l&&t[p].r<=qr)return t[p].dat;
pushdown(p);
int mid=(t[p].l+t[p].r)>>1;
int ans=-INF;
if(ql<=mid) ans=max( ans,query(ls,ql,qr) );
if(qr>mid) ans=max( ans,query(rs,ql,qr) );
return ans;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
pre[i]=last[a[i]+(int)1e5];
last[a[i]+(int)1e5]=i;//记录最近的重复位置
}
m=read();
for(int i=1;i<=m;i++){
q[i].l=read();
q[i].r=read();
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
build(1,1,n);
int j=1;
for(int i=1;i<=n;i++){
update(1,pre[i]+1,i,a[i]);
for(;j<=m&&q[j].r<=i;j++)
ans[q[j].id]=query(1,q[j].l,q[j].r);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
GSS3单点修改+查询区间最大子段和
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 50005;
#define ls (p<<1)
#define rs (p<<1|1)
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct node{
LL lmx,rmx,lrs,sum;
}t[N<<2];
int n,m;
inline void pushup(int p) {
t[p].sum=t[ls].sum+t[rs].sum;
t[p].lmx=max(t[ls].lmx,t[rs].lmx+t[ls].sum);
t[p].rmx=max(t[rs].rmx,t[ls].rmx+t[rs].sum);
t[p].lrs=max(max(t[ls].lrs,t[rs].lrs),t[ls].rmx+t[rs].lmx);
}
void build(int l,int r,int p) {
if(l==r){
t[p].sum=read();
t[p].lmx=t[p].rmx=t[p].lrs=t[p].sum;return;
}
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
pushup(p);
}
void update(int k,int v,int l,int r,int p) {
if(l==r) {
t[p].lmx=t[p].rmx=t[p].lrs=t[p].sum=v;return;
}
int mid=(l+r)>>1;
if(k<=mid) update(k,v,l,mid,ls);
if(k>mid) update(k,v,mid+1,r,rs);
pushup(p);
}
node query(int L,int R,int l,int r,int p) {
if(L<=l&&r<=R) {
return t[p];
}
int mid=(l+r)>>1;
node vis,f1,f2;
vis.sum=0;
if(L<=mid) f1=query(L,R,l,mid,ls),vis=f1;
if(R>mid) f2=query(L,R,mid+1,r,rs),vis=f2;
if(L<=mid&&R>mid) {
vis.sum=f1.sum+f2.sum;
vis.lmx=max(f1.lmx,f1.sum+f2.lmx);
vis.rmx=max(f2.rmx,f1.rmx+f2.sum);
vis.lrs=max(max(f1.lrs,f2.lrs),f1.rmx+f1.lmx);
}
return vis;
}
int main() {
n=read();
build(1,n,1);
m=read();
while(m--) {
int k=read(),x=read(),y=read();
if(k==1){
if(x>y) swap(x,y);
node ans=query(x,y,1,n,1);
printf("%d\n",ans.lrs);
} else update(x,y,1,n,1);
}
return 0;
}
GSS5
查询左端点在$ [x_1, y_1]\(之间,且右端点在\) [x_2, y_2]$之间的最大子段和,数据保证x1≤x2,y1≤y2 ,但是不保证端点所在的区间不重合
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
#define INF 1e9+7
#define ls p<<1
#define rs p<<1|1
inline int read(){
int f=1,x=0; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
return f*x;
}
int min(int x,int y){
return x>y?y:x;
}
int max(int x,int y){
return x>y?x:y;
}
int n,T,m,a[N];
struct TREE{
int lmx,rmx,sum,dat;
}t[N<<2];
inline TREE merge(TREE x,TREE y){
TREE res;
res.lmx=max(x.lmx,x.sum+y.lmx);
res.rmx=max(y.rmx,y.sum+x.rmx);
res.sum=x.sum+y.sum;
res.dat=max(max(x.dat,y.dat),x.rmx+y.lmx);
return res;
}
inline void build(int p,int l,int r){
if(l==r){
t[p].dat=t[p].sum=t[p].lmx=t[p].rmx=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,1+mid,r);
t[p]=merge(t[ls],t[rs]);
}
inline TREE get(int p,int l,int r,int ql,int qr){
if(ql>qr)return (TREE){0,0,0,0};
if(l==ql&&r==qr)return t[p];
int mid=(l+r)>>1;
if(ql>mid) return get(rs,mid+1,r,ql,qr);
else if(qr<=mid) return get(ls,l,mid,ql,qr);
else return merge(get(ls,l,mid,ql,mid),get(rs,mid+1,r,mid+1,qr));
}
inline int query(int x1,int y1,int x2,int y2){
if(y1<x2){
return get(1,1,n,x1,y1).rmx+get(1,1,n,y1+1,x2-1).sum+get(1,1,n,x2,y2).lmx;
}else{
int ans=get(1,1,n,x2,y1).dat;
if(x1<x2)ans=max(ans,get(1,1,n,x1,x2).rmx+get(1,1,n,x2,y2).lmx-a[x2]);
if(y2>y1)ans=max(ans,get(1,1,n,x1,y1).rmx+get(1,1,n,y1,y2).lmx-a[y1]);
return ans;
}
}
int main(){
T=read();
while(T--){
memset(t,0,sizeof(t));
n=read();
for(int i=1;i<=n;i++)a[i]=read();
build(1,1,n);
m=read();
while(m--){
int x1,x2,y1,y2;
x1=read();y1=read();x2=read();y2=read();
printf("%d\n",query(x1,y1,x2,y2));
}
}
return 0;
}