---恢复内容开始---
这道题真的是非常恶心,看题解看了半天才弄懂,而且题解上说的相当简略。
此题大意是询问去掉重复元素的最大子区间和,没有修改操作。
没有修改操作,这样就可以离线处理了。
这道题有几个难点:
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; }
---恢复内容结束---