spoj GSS2 (线段树) - xgtao -

题目链接

这是一道线段树的题目,维护历史版本,给出N(<=100000)个数字(-100000<=x<=100000),要求求出在[l,r]区间里面的连续序列的最大值,并且重复的数字可以加入序列但是值不能再计算。

数据范围以及区间的查询提示使用线段树,但是我们怎么建树呢?在线操作是没法做的。那就离线吧。

定义s[i] = ai + ai+1 + ai+2 + ... an,以ai开头的数列的和,那么每次加入更新ai 那么s1,s2,...si都会相应的加一个ai,s[1~i]中出现过a[i]是不能重复加值的,那么为了避免重复加值,用pre[a[i]]表示a[i]上一次出现的位置,那么也就是s[pre[ai]+1]~s[i]这一个区间加上a[i],每一次更新a[i]都要记录历史版本的最大值和懒惰标记的最大值。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100010;
const int M = 100010;
const int C = 100001;
long long res[N];
int a[N],pre[N<<1],n,m;

struct Que{
	int l,r,ID;
	bool operator < (const Que &rhs)const{return r < rhs.r;}
}question[M];

struct Tree{long long s,ms,d,md;}tree[N<<2];

#define lson k<<1,l,mid
#define rson k<<1|1,mid+1,r

void processup(int k){
	tree[k].s = max(tree[k<<1].s,tree[k<<1|1].s);
	tree[k].ms = max(tree[k<<1].ms,tree[k<<1|1].ms);
}

void processdown(int k){
	if(!tree[k].d && !tree[k].md)return;
	tree[k<<1].ms = max(tree[k<<1].ms,tree[k<<1].s+tree[k].md);
	tree[k<<1].md = max(tree[k<<1].md,tree[k<<1].d+tree[k].md);
	tree[k<<1].s += tree[k].d,tree[k<<1].d += tree[k].d;

	tree[k<<1|1].ms = max(tree[k<<1|1].ms,tree[k<<1|1].s+tree[k].md);
	tree[k<<1|1].md = max(tree[k<<1|1].md,tree[k<<1|1].d+tree[k].md);
	tree[k<<1|1].s += tree[k].d,tree[k<<1|1].d += tree[k].d;

	tree[k].d = tree[k].md = 0;
}

long long query(int k,int l,int r,int xl,int xr){
	if(l == xl && r == xr)return tree[k].ms;
	processdown(k);
	int mid = (l+r)>>1;
	if(xr <= mid)return query(lson,xl,xr);
	else if(xl > mid)return query(rson,xl,xr);
	else return max(query(lson,xl,mid),query(rson,mid+1,xr));
	processup(k);
}

void update(int k,int l,int r,int xl,int xr,long long x){
	if(l == xl && r == xr){
		tree[k].s += x;
		tree[k].d += x;
		tree[k].ms = max(tree[k].ms,tree[k].s);
		tree[k].md = max(tree[k].md,tree[k].d);
		return;
	}
	processdown(k);
	int mid = (l+r)>>1;
	if(xr <= mid)update(lson,xl,xr,x);
	else if(xl > mid)update(rson,xl,xr,x);
	else update(lson,xl,mid,x),update(rson,mid+1,xr,x);
	processup(k);
}

#define clr(a,b) memset(a,b,sizeof(a))

int main(){
	while(scanf("%d",&n) == 1){
		clr(tree,0),clr(pre,0);
		for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
		scanf("%d",&m);
		for(int i = 1;i <= m;++i){
			scanf("%d%d",&question[i].l,&question[i].r);
			question[i].ID = i;
		}
		sort(question+1,question+m+1);
		int ID = 1;
		for(int i = 1;i <= n;++i){
			update(1,1,n,pre[a[i]+C]+1,i,a[i]);
			pre[a[i]+C] = i;
			while(ID <= m && question[ID].r == i){
				res[question[ID].ID] = query(1,1,n,question[ID].l,question[ID].r);
				ID++;
			}
		}
		for(int i = 1;i <= m;++i)printf("%lld\n",res[i]);
	}
	return 0;
}

  

  

时间: 2024-08-26 02:05:24

spoj GSS2 (线段树) - xgtao -的相关文章

SPOJ GSS3 线段树系列1

SPOJ GSS系列真是有毒啊! 立志刷完,把线段树搞完! 来自lydrainbowcat线段树上的一道例题.(所以解法参考了lyd老师) 题意翻译 n 个数, q 次操作 操作0 x y把 Ax 修改为 y 操作1 l r询问区间 [l,r] 的最大子段和 数据规模在50000,有负数. 冷静分析 因为要维护最大子段和,那么我们可以在线段树struct中维护这么几个信息: sum(区间和).lmax(从左顶点出发的最大子段和).rmax(从右顶点出发的最大子段和).maxx(这段的最大子段和)

spoj GSS线段树以及二维树状数组合集

T1 维护lmax 向左延伸的最大值,rmax同理,sum区间和,ans答案. 转移见operator + #include<bits/stdc++.h> #define mid (l+(r-l)/2) #define ls (rt<<1) #define rs (rt<<1|1) #define int long long using namespace std; const int N =(int)1e5+10; struct TREE { int lef,rig,

spoj 1043 线段树

线段树在解决区间合并问题上还是很强力的,每个结点维护三个值:maxl, maxr, maxn,然后合并操作见pushup函数. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 50001; 7 int a[N]; 8 int sum[N]; 9 10 struct Node 11 { 12 int l, r; 1

spoj 2713 线段树

给定一个序列,有两种操作:对一个区间内的数字开方和求区间内所有数字的和.注意到一个即使很大的数经过没几次开方操作以后就会变成1,而1开方还是1.所以可以用线段树来维护,对于那些全部都是1的区间(即区间和等于区间长度)我们不用更新,剩下的就是区间求和了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 using namespace std;

SPOJ BGSHOOT 线段树

BGSHOOT - Shoot and kill The problem is about Mr.BG who is a great hunter. Today he has gone to a dense forest for hunting and killing animals. Sadly, he has only one bullet in his gun. He wants to kill as many animals as possible with only one bulle

支点旋转(自创题 &amp; 线段树) - xgtao -

Description在一个平面上有N 个首位相连的杠杆.初始所有杠杆都为1 个单位长度.第一根杠杆左侧位于(0,0)处.所有杠杆水平放置. 现在我会对这些杠杆进行以下两种操作:1.拉伸此操作标号为1,意为将某根杠杆沿原放向伸长x 个单位. 2.旋转此标号操作为2,意为将某根杠杆逆时针伸长x 度. 现在,我关心的是每次操作后第N 个杠杆的右侧在哪里呢? Input第一行两个整数N,M 表示杠杆数和操作数.接下来若干行,每行三个数x,y,z.若x=1,表示对第y 根杠杆伸长z 个单位.若x=2,表

bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145  Solved: 76[Submit][Status][Discuss] Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和(可选空子段). 这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次. 比如:1,2,3,2,2,2出现了3次,但只算一次,

SPOJ GSS2 - Can you answer these queries II(线段树 区间修改+区间查询)(后缀和)

GSS2 - Can you answer these queries II #tree Being a completist and a simplist, kid Yang Zhe cannot solve but get Wrong Answer from most of the OI problems. And he refuse to write two program of same kind at all. So he always failes in contests. When

spoj gss2 : Can you answer these queries II 离线&amp;&amp;线段树

1557. Can you answer these queries II Problem code: GSS2 Being a completist and a simplist, kid Yang Zhe cannot solve but get Wrong Answer from most of the OI problems. And he refuse to write two program of same kind at all. So he always failes in co