GSS2-Can you answer these queries II

---恢复内容开始---

这道题真的是非常恶心,看题解看了半天才弄懂,而且题解上说的相当简略。

此题大意是询问去掉重复元素的最大子区间和,没有修改操作。

没有修改操作,这样就可以离线处理了。

这道题有几个难点:

1.怎么表示去掉重复元素的区间和?

有一种简便而且高效的方法,用pos[a[i]]表示a[i]上次出现的位置,用s[j]表示从j到i(当前处理的元素)的去掉重复元素的区间和.每次加入一个数a[i],将s[pos[a[i]]+1]到s[i]全部加上a[i](这里要用到线段树的区间更新),就表示出了去掉重复元素的区间和,顺着这个思路想下去,当我们把a[i]加进来的时候,如果i恰好是询问的区间的有端点,那么s[L]到s[R]所有出现过的值中的最大值就是我们要求的答案.

2.为什么要对区间进行排序?

假设一个询问是(1,3) 当我们把a[4]加进来的时候,我们会改变s[pos[a[4]]+1]到s[4]所有的值,如果pos[a[4]]+1<=3,那么s[pos[a[4]]+1]就被a[4]影响了,讲导致我们难以得出正确答案.将区间按照右端点排序过后,处理了其右端点,就记录下查询的结果,这样在处理后面的点时就不用担心影响了前面的区间.

3.怎么求出历史最大值?

   我们需要维护四个值

              max_now:去掉重复元素的区间和            max_old:历史最大值

              tag_now:区间上的更新                        tag_old:区间上的最大更新(是针对整个区间的更新,不是点)

tag_now和tag_old实际上就是max_now和max_old的标签,tag_now很好理解,操作起来和普通的区间更新一样。tag_old比较迷,要弄清楚一点,我们维护tag_old的目的是为了得出max_old的值。

我们来详细看下51行这句代码:

    tag_old[rt]=max(tag_old[rt],tag_now[rt]+=add);

这其实有两个命令,tag_now[rt]+=add;   tag_old[rt]=max(tag_old[rt],tag_now[rt]);第一个命令很好理解,就是普通的标签。第二句就体现了tag_old的意义:记录最大的更新值,注意:每次更新都是针对一个连续的区间的。

这里的代码为什么不是tag_old[rt]=max(tag_old[rt],tag_old[rt]+add);呢?因为这样做有可能把区间割裂开,比如说有4个数是2 -1 -3 1执行这个命令就相当于把2加进去的时候tag_old[1]=2,然后加-1和-3的时候tag_old[1]不会更新,把1加进去的时候又会更新tag_old[1]=2+1=3,此时,就相当于把2和1加起来了而没有加上-1和-3,不再是一个连续的区间!

在向下传递tag的时候也是同样的道理tag_old和max_old要么不变,要么就可以由当前的区间来更新,注意不是点。向下传递tag的代码也可以用类型的思路来理解,要时刻保持区间的连续性。

最后注意一点,就是传递tag时的顺序,必须先更新tag_old和max_old,因为更新这两个的时候要用到tag_now和max_now,如果先改变了tag_now和max_now,就相当于已经把区间上所有的元素加起来了,显然不是“最大自区间”。

代码如下:

#include<bits/stdc++.h>
#define rep(i,n) for(i=1;i<=n;i++)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define creatmid int mid=(l+r)>>1
using namespace std;
typedef long long int ll;
const int N=1e5+5;
ll max_now[N*4],max_old[N*4],tag_now[N*4],tag_old[N*4],a[N],ans[N];
int n,q,pos[N*2+5];
struct zz
{
	int L,R,id;
	bool operator<(const zz&u)const
	{
		return R<u.R;
	}
}Q[N];
void PushUp(int rt)
{
	//if(rt==13)printf("%d\n",tag_old[rt]);
	max_now[rt]=max(max_now[rt<<1],max_now[rt<<1|1]);
	max_old[rt]=max(max_old[rt<<1],max_old[rt<<1|1]);
}
void PushDown(int l,int r,int rt)
{
	if(l==r)
	{
		tag_old[rt]=0;
		tag_now[rt]=0;
		return ;
	}
	tag_old[rt<<1]=max(tag_old[rt<<1],tag_now[rt<<1]+tag_old[rt]);
	tag_old[rt<<1|1]=max(tag_old[rt<<1|1],tag_now[rt<<1|1]+tag_old[rt]);

	max_old[rt<<1]=max(max_old[rt<<1],max_now[rt<<1]+tag_old[rt]);
	max_old[rt<<1|1]=max(max_old[rt<<1|1],max_now[rt<<1|1]+tag_old[rt]);

	max_now[rt<<1]+=tag_now[rt];max_now[rt<<1|1]+=tag_now[rt];
	tag_now[rt<<1]+=tag_now[rt];tag_now[rt<<1|1]+=tag_now[rt];

	tag_old[rt]=tag_now[rt]=0;
	//if(rt==13)printf("%d\n",tag_old[rt]);
}
void update(int L,int R,ll add,int l,int r,int rt)
{
	//if(rt==13)
//	printf("L=%d R=%d add=%lld l=%d r=%d rt=%d\n",L,R,add,l,r,rt);
	if(l>=L && r<=R)
	{
		tag_old[rt]=max(tag_old[rt],tag_now[rt]+=add);
		max_old[rt]=max(max_old[rt],max_now[rt]+=add);
		return ;
	}
//	if(rt==14)printf("%d\n",tag_old[rt]);
	PushDown(l,r,rt);
	creatmid;
	if(mid>=R)update(L,R,add,lson);
	else if(mid<L)update(L,R,add,rson);
	else
	{
		update(L,R,add,lson);
		update(L,R,add,rson);
	}
	PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
//	printf("L=%d R=%d l=%d r=%d rt=%d old=%lld\n",L,R,l,r,rt,max_old[rt]);
	if(l>=L && r<=R)return max_old[rt];
	PushDown(l,r,rt);
	creatmid;
	if(mid>=R)return query(L,R,lson);
	if(mid<L)return query(L,R,rson);
	return max(query(L,R,lson),query(L,R,rson));
}
int main()
{
	int i;
	scanf("%d",&n);
	rep(i,n)
		scanf("%lld",&a[i]);
	scanf("%d",&q);
	rep(i,q)
	{
		scanf("%d%d",&Q[i].L,&Q[i].R);
		Q[i].id=i;
	}
	sort(Q,Q+q+1);
	// rep(i,q)
	// 	printf("L=%d R=%d\n",Q[i].L,Q[i].R);
	int num=1;
	rep(i,n)
	{
		update(pos[a[i]+N]+1,i,a[i],1,n,1);
		pos[a[i]+N]=i;
		for(;Q[num].R==i && num<=q;num++)
			ans[Q[num].id]=query(Q[num].L,Q[num].R,1,n,1);
		if(num>q)break;
	}
//	printf("%d\n",tag_old[8]);
	rep(i,q)
		printf("%lld\n",ans[i]);
	return 0;
}

  

 

---恢复内容结束---

时间: 2024-10-09 01:59:35

GSS2-Can you answer these queries II的相关文章

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

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

Time Limit: 1000MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Description 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

SPOJ 1557. Can you answer these queries II 线段树

Can you answer these queries II Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 https://www.spoj.com/problems/GSS2/ Description Being a completist and a simplist, kid Yang Zhe cannot solve but get Wrong Answer from most of the OI problems. And he refuse

BZOJ2482: [Spoj1557] Can you answer these queries II

题解: 从没见过这么XXX的线段树啊... T_T 我们考虑离线做,按1-n一个一个插入,并且维护区间[ j,i](i为当前插入的数)j<i的最优值. 但这个最优值!!! 我们要保存历史的最优值,以及当前的最优值!!!还有lazy!!!也得分历史和现在!!T_T 怎么搞!!! inline void update(int k,int z1,int z2) { t[k].tag[1]=max(t[k].tag[1],t[k].tag[0]+z2); t[k].tag[0]+=z1; t[k].mx

【BZOJ2482】[Spoj1557] Can you answer these queries II 线段树

[BZOJ2482][Spoj1557] Can you answer these queries II Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和(可选空子段). 这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次. 比如:1,2,3,2,2,2出现了3次,但只算一次,于是这个序列的和是1+2+3=6. Input 第一行一个数n. 第二行n个数,为给定的序列,这些数的绝对值小于等于100000. 第三行一个数m. 接下来m行,每行两个

【SPOJ - GSS2】Can you answer these queries II(线段树)

区间连续不重复子段最大值,要维护历史的最大值和当前的最大值,打两个lazy,离线 #include<cstdio> #include<cstring> #include<algorithm> #define maxn 150000 #define rep(i,l,r) for(int i=l;i<=r;i++) #define LL long long using namespace std; typedef struct { LL nmax,hmax,nlazy

Can you answer these queries II

很好的一道题,想了很久.首先突破的是,可以找到枚举其中一边,假设是尾部y,然后快速找出满足条件的最大的头部x,连续区间的和 很容易想到借助部分和的思想,如果是从y开始往前面累加,那么就是一个关于y的后缀和. 如果答案的一边在y,那么就转化成了对后缀和的RMQ. y的后缀和y-1或者y+1的后缀是差不多的,只要想想怎么递推就好了. y == 0时的所有后缀是很容易得到的,因此考虑用y递推y+1,这个后缀和是从右边往左累加的,重复的不算, 对于元素a[y+1],它所影响的后缀只有y+1到a[y+1]