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;
}

posted @ 2020-08-14 00:06  ke_xin  阅读(42)  评论(0编辑  收藏  举报