【CodeVs 6128 Lence的方块们】
·希望除了内部人员以外能有人通过这道题,因为这是大米饼第一次改编的题
·我所见到的“本题原版”的题解也很少,搜索一下应该是:LEIJP开船
·分析:
简化题目,可以了解到题目要求的是:当前序列中最长的一段等差数列(当然序列存的是当前每个方块槽里的方块数量)。但是方块槽数大,操作次数多(<=500000),所以需要寻找一种美妙的数据结构来维护。区间上常用线段树,因为这道题给你的感觉是:亲切而又陌生。亲切在于它类似于以前线段树练习中求最长相同颜色子序列或者最大和连续子序列。让我们回忆一下这类型题的处理方式:用lsum[u],rsum[u],sum[u]分别表示从左起的最长序列,从右起最长序列,以及该序列的最长序列(这就是最终答案)。
这道题的特色在于:怎样维护满足条件的区间呢?因为每次操作是给一个区间每个位置加上不相同的数,这样就没法区间操作了!就在闪亮的眼泪快要落下来的时候,你发现了一个美妙之处:等差数列的特色是公差是一个定值。你欣喜的将这个“相同的数据”拽在手里,是的如果用线段树上每一个点表示相邻方块槽的数量差值,那么如果是等差数列,拿这些点的值就是有相同的。每次你的加入操作,也可以直接加入公差b了!如一下图:
·这样做你就只需要找最长的元素相同区间(就是以前的颜色相同一段)。
怎么做?每次加入操作就向区间[l,r]统一加上b(Lazy操作等你来领取)
【哦,这个区间长度加1才是答案哦,哦,哦】
·就在你狂喜之际,你发现了一个小问题:每次操作就加公差,好像多加几次会出现神秘的误判问题,如两下图:
·问题出在哪里?首先,那个“0”是因为你做了两次区间操作,没有涉及到0右边的那个区间,所以没有加上任何公差,它处于初始化状态。正是这个原因踏实的正确答案从3卡成了6。图中的错误根源在于:最高的绿块和最矮的蓝块之间是1!它本应该是-2,但是加公差操作不能完成这个任务。
·总结地说,当前处理方式存在这样一个问题:
在开始的思路中,我们想让数值相同的连续区间表示一个等差数列。但是只加入公差就是我们只能了解到每次插入的序列的公差,却无从了解它们a值的关系(a就是题目中所说的,可以看做是等差数列的起始项)。
·美妙的单点修改拯救世界:
观赏下面这样一种状态(也就是输入样例):
在黄色方块们假如以后,这明显的构成了一个跨度为5的大阶梯。比Lence的说明书上的阶梯好看多了。仔细冷静地观察现在的线段树情况:
错误已经暴露,解决的时候到了。对于绿色部分(推广地说就是同一次加入的方块们)他们之间是”连续的”,即相邻两块之间的差值不是b就是-b。然而遭遇了黄色方块后我们不能保证黄绿交界处能否继续连续下去。处理方法是将图中线段树3位置加上一个黄色方块团的a,如果这样一加,就只有在“连续”区间时该点的值和周围的相同。类似地,在每次区间操作的末尾的后一个点减去这个槽位的方块数,可以推算出方块数为:a+(l+r-2*p)*b
如果后来的方块团要与这个团拼接成更大的阶梯,就要以自己前面单独加的数(就上上文加的a)和前面加的那个方块团末尾减去的值相互抵消,同样的道理,只有最终线段树该点的值和周围的点相同时,才可以连成阶梯。(感性地说,你可以理解为在地上挖一个坑,后来的方块来填坑)
最后解释一下代码中的数组作用:
lsum[u],rsum[u],sum[u]同上。lnum[u]表示当前线段树区间左端点的值,
rnum[u]表示线段树当前右端点的值(这两个的作用就是弥补只加上公差而带来的不足)。还有一个懒数组(Lazy[u])这就是懒惰操作。
Lazy数组:你才懒,大米饼你凭什么说我懒?没有我你的区间操作就炸了!
1 #include<stdio.h> 2 #include<algorithm> 3 #define go(i,a,b) for(int i=a;i<=b;i++) 4 using namespace std;const int N=400003; 5 struct Convey{int Lnum,Rnum,Lsum,Rsum;}__; 6 int n,m,sz,lch[N],rch[N],lsum[N],rsum[N],_; 7 int sum[N],lazy[N],lnum[N],rnum[N]; 8 void build(int &u,int l,int r){ 9 u=++sz;sum[u]=lsum[u]=rsum[u]=r-l+1;if(l==r)return; 10 int mid=l+r>>1;build(lch[u],l,mid);build(rch[u],mid+1,r); 11 } 12 void Down(int u){ 13 lnum[lch[u]]+=lazy[u];rnum[lch[u]]+=lazy[u]; 14 lnum[rch[u]]+=lazy[u];rnum[rch[u]]+=lazy[u]; 15 lazy[lch[u]]+=lazy[u];lazy[rch[u]]+=lazy[u];lazy[u]=0; 16 } 17 void Up(int u,int l,int r){int mid=l+r>>1; 18 sum[u]=max(sum[lch[u]],sum[rch[u]]); 19 lnum[u]=lnum[lch[u]];lsum[u]=lsum[lch[u]]; 20 rnum[u]=rnum[rch[u]];rsum[u]=rsum[rch[u]]; 21 if(lnum[rch[u]]==rnum[lch[u]]&&lsum[u]==mid-l+1)lsum[u]+=lsum[rch[u]]; 22 if(lnum[rch[u]]==rnum[lch[u]]&&rsum[u]==r-mid+0)rsum[u]+=rsum[lch[u]]; 23 if(lnum[rch[u]]==rnum[lch[u]])sum[u]=max(sum[u],rsum[lch[u]]+lsum[rch[u]]); 24 } 25 void update(int u,int l,int r,int L,int R,int val){ 26 if(l==L&&r==R){lnum[u]+=val;rnum[u]+=val;lazy[u]+=val;return;} 27 if(lazy[u])Down(u);int mid=l+r>>1; 28 if(R<=mid)update(lch[u],l,mid,L,R,val); 29 else if(L>mid)update(rch[u],mid+1,r,L,R,val); 30 else update(lch[u],l,mid,L,mid,val), 31 update(rch[u],mid+1,r,mid+1,R,val);Up(u,l,r); 32 } 33 int find(int u,int l,int r,int L,int R,Convey& car){ 34 if(l==L&&r==R){car=(Convey){lnum[u],rnum[u],lsum[u],rsum[u]};return sum[u];} 35 if(lazy[u])Down(u);int mid=l+r>>1; 36 if(R<=mid)return find(lch[u],l,mid,L,R,car); 37 else if(L>mid)return find(rch[u],mid+1,r,L,R,car); 38 else{Convey A,B;int ans; 39 ans=max(find(lch[u],l,mid,L,mid,A),find(rch[u],mid+1,r,mid+1,R,B)); 40 car.Lnum=A.Lnum;car.Rnum=B.Rnum;car.Lsum=A.Lsum;car.Rsum=B.Rsum; 41 if(A.Rnum==B.Lnum&&car.Lsum==mid-l+1)car.Lsum+=B.Lsum; 42 if(A.Rnum==B.Lnum&&car.Rsum==r-mid+0)car.Rsum+=A.Rsum; 43 if(A.Rnum==B.Lnum)ans=max(ans,A.Rsum+B.Lsum);return ans; 44 }Up(u,l,r); 45 } 46 int main(){scanf("%d%d",&n,&m);n--;build(_,1,n); 47 go(i,1,m){int f,l,r,a,k,p;scanf("%d",&f); 48 if(!f){scanf("%d%d%d%d%d",&l,&r,&a,&k,&p); 49 if(p-1>=l)update(1,1,n,l,p-1,k); 50 if(p+1<=r)update(1,1,n,p,r-1,-k); 51 if( l>=2 )update(1,1,n,l-1,l-1,a); 52 if( r<=n )update(1,1,n,r,r,-(2*p-l-r)*k-a); 53 } 54 if(f)scanf("%d%d",&l,&r),l==r?puts("1"):printf("%d\n",1+find(1,1,n,l,r-1,__)); 55 }return 0;}//Paul_Guderian
当黎明和落日的光影交错的时刻,我们纷纷逃出每一座尖叫的城市。———汪峰《信仰在空中飘扬》