数列分块入门 3
给出一个长为n的数列,以及n个操作,操作涉及区间加法,
询问区间内小于某个值x的前驱(比其小的最大元素)。
Input
第一行输入一个数字n。
第二行输入n个数字,第ii个数字为a_i,以空格隔开。
接下来输入n行询问,每行输入四个数字opt、l、r、c,以空格隔开。
若opt=0,表示将位于[l,r]的之间的数字都加c。
若opt=1,表示询问[l,r]中c的前驱的值(不存在则输出-1)。
1<=n<=100000,−2^31<=others<=2^31
Output
对于每次询问,输出一行一个数字表示答案。
Sample Input
4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
Sample Output
3
-1
//利用set中的lowerbound进行查找第一个大于等于指定c的值 #pragma GCC optimize(3) #include<bits/stdc++.h> #include<set> #define N 100001 using namespace std; set<int> tree[1100]; int n,m,pos[N]; int s[N],tag[N]; void read(int &x) { char ch; bool ok; for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1; for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x; } void add(int l,int r,int c) { for(int i=l;i<=min(pos[l]*m,r);i++) { tree[pos[i]].erase(s[i]); s[i]+=c; tree[pos[i]].insert(s[i]); } if(pos[l]!=pos[r]) for(int i=(pos[r]-1)*m+1;i<=r;i++) { tree[pos[i]].erase(s[i]); s[i]+=c; tree[pos[i]].insert(s[i]); } for(int i=pos[l]+1;i<=pos[r]-1;i++) tag[i]+=c; } int findfront(int l,int r,int c) { int ans=-1,num; for(int i=l;i<=min(pos[l]*m,r);i++)//不成块的区间暴力操作 { num=tag[pos[i]]+s[i]; if(num<c) ans=max(ans,num);//找出比c小的值 } if(pos[l]!=pos[r]) for(int i=(pos[r]-1)*m+1;i<=r;i++) { num=tag[pos[i]]+s[i]; if(num<c) ans=max(ans,num);//找出比c小的值 } for(int i=pos[l]+1;i<=pos[r]-1;i++) //成块的区间,其内部元素值并没有发生变化,只是加上了lazy标记 { int t=c-tag[i]; set<int>::iterator k=tree[i].lower_bound(t); if(k==tree[i].begin()) continue; k--; ans=max(ans,*k+tag[i]); } return ans; } int main() { read(n);m=sqrt(n); for(int i=1;i<=n;i++) pos[i]=(i-1)/m+1; //数字i在哪一个块中 for(int i=1;i<=n;i++) read(s[i]),tree[pos[i]].insert(s[i]); for(int i=1;i<=n;i++) { int way,l,r,c; read(way),read(l),read(r),read(c); if(way==0) //[l,r]的之间的数字都加c add(l,r,c); if(way==1) //询问[l,r]中c的前驱的值 printf("%d\n",findfront(l,r,c)); } return 0; }