题目:
给定n个数,q个操作(n<=500000 q<=50000)。
操作有:
1 L R x :将[L,R]的数都加上x
2 y 询问第一个y和最后一个y的横坐标差,若不存在y则返回-1
这题想了很久。可以实现区间修改的结构都没办法支持50000个对y的询问。
线段树,树套树之类的都想了,都做不出来。然后就只剩一种做法了:分块。
看了题解,果然是分块,于是就开始写分块了。
每段维护两个序列,一个原始顺序,一个排序后的序列,以及一个修改标记Add。
把整个区间分成sqrt(n)块,如果修改的区间完全包含每一块,那么只需要修改Add标记,在查询的时候,外部查询y,实际上要查询y-Add。
如果包含某区间的一部分,那么就修改原数组,然后重新排序。
查询的话,先按块内排序后的结果二分查找存不存在y,再在每块的原序列从左往后或从右往左查找y值就行了。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <cstring>
#include <algorithm>
#define out(i) <<#i<<"="<<(i)<<" "
#define OUT1(a1) cout out(a1) <<endl
#define OUT2(a1,a2) cout out(a1) out(a2) <<endl
#define OUT3(a1,a2,a3) cout out(a1) out(a2) out(a3)<<endl
#define maxn 500007
#define LL long long
using namespace std;
LL FloorSqrt(LL x){
LL L=1,R=708,M;//[L,R) last M*M <=x;
while((L+1)^R){
M=(L+R)>>1;
if(M*M <= x) L=M;
else R=M;
}
return L;
}
struct Node{
LL A[710];//原始
LL B[710];//排序
LL Add,N;
void Sort(){sort(B,B+N);}
void Change(int t,LL V){//[0,t)+=V
for(int i=0;i<t;++i) A[i]+=V;
for(int i=0;i<N;++i) B[i]=A[i];
sort(B,B+N);
}
bool Search(LL y){
int L=0,R=N;//[L,R) last <= y
if(B[0]>y) return false;
while((L+1)^R){
int M=(L+R)>>1;
if(B[M]<=y) L=M;
else R=M;
}
return B[L]==y;
}
int SearchL(LL y){//search y --lest most
for(int i=0;i<N;++i) if(A[i]==y) return i;
return -1;
}
int SearchR(LL y){//search y --right most
for(int i=N-1;i>=0;--i) if(A[i]==y) return i;
return -1;
}
void Show(){
for(int i=0;i<N;++i) cout<<A[i]<<" ";cout<<endl;
for(int i=0;i<N;++i) cout<<B[i]<<" ";cout<<endl<<endl;
}
}X[710];
int D;//block size
int k,r;//[0,k)+[k,r)
int n,q;
void Inc(int x,LL v){//Increase [0,x] with v
int G=x/D,R=x%D+1;
for(int i=0;i<G;++i) X[i].Add+=v;
X[G].Change(R,v);
}
void Work1(int L,int R,LL v){//Increase [L,R] with X
Inc(R,v);
if(L) Inc(L-1,-v);
}
int Work2(LL y){//find first and last position of y
int Lk=-1,Rk=-1;
for(int i=0;i<=k;++i){
if(X[i].Search(y-X[i].Add)){
if(!~Lk) Lk=i*D+X[i].SearchL(y-X[i].Add);
Rk=i*D+X[i].SearchR(y-X[i].Add);
}
}
if(~Lk) return Rk-Lk;
else return -1;
}
int main(void)
{
ios::sync_with_stdio(false);
while(cin>>n>>q){--n;
D=FloorSqrt(n);
k=n/D;r=n%D+1;
for(int i=0;i<=k;++i) X[i].N=i==k?r:D,X[i].Add=0;
for(int i=0;i<=k;++i){
for(int j=0;j<X[i].N;++j) {
cin>>X[i].A[j];
X[i].B[j]=X[i].A[j];
}
X[i].Sort();
}
for(int i=0;i<q;++i){
int op,L,R,x,y;
cin>>op;
if(op==1){
cin>>L>>R>>x;
Work1(L-1,R-1,x);
}
else{
cin>>y;
cout<<Work2(y)<<endl;
}
}
}
return 0;
}