前段时间看的莫队算法,对分块法已经有所了解
这次做完了这道题,感觉分块法就是逆天....
题意:
两种操作
1 l r x表示区间[l,r]里的数都增加x
2 y表示在区间[1,n]中寻找等于y的最大下标j和最小下标i,答案就是j-i,如果不存在就输出-1
思路:
把n个数分成sqrt(n)块,维护每一个块内部的数值单调递增
每次1操作,设l在L块,r在R块,那么L+1和R-1的所有块的懒惰标记col全部加上y,L和R块的,只在原下标在[l,r]范围内的加上y,并且最后再次排序,总是使每个块内部单调递增
每次2操作,因为每个块内部都是单调递增的,那么可以直接二分第x-col[i]个区间,col[i]表示这个块每个数已经都被加了多少
代码写的比较怂......
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL,int> PII;
const int MX=500000+5;
#define Val first
#define Pid second
PII A[MX];
LL col[MX];
int n,m,unit;
void build(){
memset(col,0,sizeof(col));
for(int i=1;i<=unit;i++){
int l=i*i,r=min((i+1)*(i+1)-1,n);
sort(A+l,A+r+1);
}
}
void update(int l,int r,LL x){
int L=sqrt(l+0.5),R=sqrt(r+0.5),ll,rr;
for(int i=L+1;i<=R-1;i++){
col[i]+=x;
}
ll=L*L;rr=min((L+1)*(L+1)-1,n);
for(int i=ll;i<=rr;i++){
if(l<=A[i].Pid&&A[i].Pid<=r) A[i].Val+=x;
}
sort(A+ll,A+rr+1);
if(L==R) return;
ll=R*R;rr=min((R+1)*(R+1)-1,n);
for(int i=ll;i<=rr;i++){
if(l<=A[i].Pid&&A[i].Pid<=r) A[i].Val+=x;
}
sort(A+ll,A+rr+1);
}
int query(LL x){
int p1,p2,ans1=-1,ans2=-1;
for(int i=1;i<=unit;i++){
int l=i*i,r=min((i+1)*(i+1)-1,n);
p1=lower_bound(A+l,A+r+1,make_pair(x-col[i],0))-A;
if(p1!=r+1&&A[p1].Val==x-col[i]){
ans1=A[p1].Pid;
break;
}
}
for(int i=unit;i>=1;i--){
int l=i*i,r=min((i+1)*(i+1)-1,n);
p2=upper_bound(A+l,A+r+1,make_pair(x-col[i],n+1))-A-1;
if(p2!=0&&A[p2].Val==x-col[i]){
ans2=A[p2].Pid;
break;
}
}
if(ans1==-1||ans2==-1) return -1;
return ans2-ans1;
}
int main(){
scanf("%d%d",&n,&m);
unit=sqrt(n+0.5);
for(int i=1;i<=n;i++){
LL t;
scanf("%I64d",&t);
A[i]=make_pair(t,i);
}
build();
while(m--){
int q,l,r;LL x;
scanf("%d",&q);
if(q==1){
scanf("%d%d%I64d",&l,&r,&x);
update(l,r,x);
}else{
scanf("%I64d",&x);
printf("%d\n",query(x));
}
}
return 0;
}