前段时间看的莫队算法,对分块法已经有所了解
这次做完了这道题,感觉分块法就是逆天....
题意:
两种操作
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; }