题意:
给出n,m(n范围是【1,5e5】、m范围是【1,5e4】)
表示原来有n个数
现在有m次操作 val范围是【1,1e9】
操作1: 参数 L,R,val 表示区间【L,R】加上val
操作2: 参数val、表示在整个区间查找值val,找到相距最远的2个val,输出下标之差,找不到输出-1
思路:
对于操作1,可以用线段树解决,但是线段树不方便实现查询某个值。。
所以得用到一种 分块法/平方分桶法 其实思想和线段树类似
就是 先 分出N/ sqrt(N)个块,每个块存sqrt(n)个元素,并且每个块内部排好序(方便操作2的二分)
用一个大小为sqrt(N)的数组,来记录对整个块一起增加的val 【mark数组】
那么对于操作1 add val to【L,R】
我们只需先求出tl=L/len; tr=R/len; tl,tr表示L,R所位于的块,
那么显然如果L,R距离很远的话,中间的块,我们都可以直接 在mark【i】加上val,表示对整个块的每一个元素都加val
然后剩下的只有 与L,R相交的两个端点块了。
这个就直接暴力一遍, 把相交的块直接clear掉,再把属于【L,BL*LEN】的元素直接加上val,然后重新构建这个 被clear的块
****注意 当tl==tr这个情况特殊处理一下就好了
-------------------------------------------------------------------------------------------------------------------------------------
那么对于操作2 query(val)
由于存在一个mark数组表示这个块整体加了多少量;
所以我们实际查询的应该是val=val-mark[i]
然后
我们直接 从第一个块 到最后一个块遍历
for (i=0;i<=N/sqrt(N);i++)
{
//在 第 i 块中二分查找 到第一个出现的val(原始下标最小),记为L
}
如果找不到 直接输出-1 ;continue;
如果找到了
for (i=N/sqrt(N);i>=0;i--)
{
//在 第 i 块中二分查找 到最靠右出现的val(原始下标最大),记为R
}
然后 R-L就是答案了
-------------------------------------------------------------------------------------------------------------------------------------
复杂度分析:
操作1:
【复杂度A】【如果tl==tr】 也就是l,r在同一个块,那么整个块元素不超过sqrt(N), 所以复杂度是sqrt(N)+ sqrt(N)* log 【sqrt(N)】 、前者是遍历更新,后者是排序
【复杂度B】【其余情况】,要操作的块分为二部分,两端不完全包含的块,和中间被完全包含的块,前者复杂度就是tl==tr的一样啦,后者是O(1),最多有N/sqrt(N)块
所以最糟糕复杂度也就是 2*【复杂度A】+N/sqrt(N);
-------------------------------------------------------------------------所以操作也总的复杂度也就是sqrt(N)* log 【sqrt(N)】级别的
操作2:
直接遍历所有块,N/sqrt(n)个块,每次二分查找 log(sqrt(N));
-------------------------------------------------------------------------所以操作也总的复杂度也是sqrt(N)* log 【sqrt(N)】级别的
所以整体复杂度 就是 m*sqrt(N)* log 【sqrt(N)】级别的
N规模是5e5,m是5e4
50000* 707* 9.46=334411000...
x
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
__int64 n,m;
struct node
{
__int64 x,num;
node(){}
node(__int64 a,__int64 b)
{
x=a;
num=b;
}
bool operator <( const node& b)const
{
if (x!=b.x)
return x<b.x;
else
return num<b.num;
}
bool operator >( const node& b)const
{
if (x!=b.x)
return x>b.x;
else
return num>b.num;
}
};
__int64 tm[5*100000+50];
__int64 mark[1005];
vector<node> sb[1005];
__int64 init()
{
__int64 i;
__int64 cnt=-1;
__int64 len=sqrt(n);
for(i=0;i<n;i++)
{
if (i%len==0) //0到len-1为第一块
cnt++;
sb[cnt].push_back(node(tm[i],i));
}
for (i=0;i<=cnt;i++)
sort(sb[i].begin(),sb[i].end());
return cnt;
}
int main()
{
__int64 op,l,r,val,i,j,k;
__int64 cnt=0;
scanf("%I64d%I64d",&n,&m);
for (i=0;i<n;i++)
{
scanf("%I64d",&tm[i]);
}
cnt=init(); //块数
__int64 len=sqrt(n); //块大小
for (k=1;k<=m;k++)
{
scanf("%I64d",&op);
if (op==1)
{
scanf("%I64d%I64d%I64d",&l,&r,&val);
l--;r--;
__int64 tl=l/len; //【0,len-1】都得到tl=1
__int64 tr=r/len;
if (!val) continue;
for (i=tl+1;i<=tr-1;i++)
mark[i]+=val;
sb[tl].clear();
sb[tr].clear();
//两端相交区间
if (tl==tr)
{
for (i=l;i<=r;i++)
tm[i]+=val;
for (i=tl*len;i<tl*len+len;i++)
sb[tl].push_back(node(tm[i],i));
sort(sb[tl].begin(),sb[tl].end());
}
else
{
for (i=l;i<tl*len+len;i++)
tm[i]+=val;
for (i=tr*len;i<=r;i++)
tm[i]+=val;
for (i=tl*len;i<tl*len+len;i++)
sb[tl].push_back(node(tm[i],i));
for (i=tr*len;i<tr*len+len;i++)
sb[tr].push_back(node(tm[i],i));
sort(sb[tl].begin(),sb[tl].end());
sort(sb[tr].begin(),sb[tr].end());
}
}
else
if (op==2)
{
scanf("%I64d",&val);
l=-1;r=0;
for (i=0;i<=cnt;i++)
{
__int64 tmp=val-mark[i];
__int64 it=upper_bound(sb[i].begin(),sb[i].end(),node(tmp,-1))-sb[i].begin();
//因为num也是比较的一部分,所以如果存在,则返回num最小的tmp
if (it==len) continue; //找不到比(tmp,-1)大的,返回end
if (sb[i][it].x!=tmp) continue;//找到比(tmp,-1)大的,但值不为tmp
l=sb[i][it].num;//num最小的tmp
break;
}
if (l==-1)
{
printf("-1\n");
continue;
}
for (i=cnt;i>=0;i--)
{
__int64 tmp=val-mark[i];
__int64 it=upper_bound(sb[i].begin(),sb[i].end(),node(tmp,5e6))-sb[i].begin();
if (it!=0) it--;
if (sb[i][it].x!=tmp) continue;
r=sb[i][it].num;
break;
}
printf("%I64d\n",r-l);
}
}
return 0;
}
第二次写的代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
inline __int64 min(__int64 a,__int64 b)
{return a<b?a:b;}
struct node
{
__int64 x,id;
node(__int64 a=0,__int64 b=0)
{x=a,id=b;}
};
bool sort_cmp(const node & a,const node & b)
{
if (a.x!=b.x)
return a.x<b.x;
return a.id<b.id;
}
bool cmp(const node & a,const node & b)
{
return a.x<b.x;
}
struct sg
{
node xx[800];
__int64 num;
__int64 val;
sg()
{num=val=0;}
};
sg tm[800];
__int64 aa[500005+50];
int main()
{
__int64 i,j;
int q,n;
cin>>n>>q;
for (i=0;i<n;i++)
scanf("%I64d",&aa[i]);
__int64 block_len=sqrt(1.0*n);
__int64 sg_num=n/block_len;
if (n%block_len)
sg_num++;
for (i=0;i<n;i++)
{
__int64 what=i/block_len;
tm[what].xx[++tm[what].num]=node(aa[i],i);
}
for (i=0;i<sg_num;i++)
sort(tm[i].xx+1,tm[i].xx+1+tm[i].num,sort_cmp);
__int64 op,x,l,r,kk;
for (kk=1;kk<=q;kk++)
{
scanf("%I64d",&op);
if (op==1)
{
scanf("%I64d%I64d%I64d",&l,&r,&x);
__int64 st=(l-1)/block_len;
__int64 ed=(r-1)/block_len;
if (!x)continue;
if (st==ed)
{
for (i=l-1;i<=r-1;i++)
aa[i]+=x;
tm[st].num=0;
__int64 len= min(st*block_len+block_len,n); //一定要限制右端点,感觉理论上不限制不会错,但是一直wa
for (i=st*block_len;i<len;i++)
tm[st].xx[++tm[st].num]=node(aa[i] ,i);
sort(tm[st].xx+1,tm[st].xx+1+tm[st].num,sort_cmp);
// tm[st].val=0; //val要么永久留在区间,要么加到aa中,不可加在node中
}
else
{
__int64 len= st*block_len+block_len ;
for (i=l-1;i<len;i++)
aa[i]+=x;
tm[st].num=0;
for (i=st*block_len;i<len;i++)
tm[st].xx[++tm[st].num]=node(aa[i] ,i);
sort(tm[st].xx+1,tm[st].xx+1+tm[st].num,sort_cmp);
// tm[st].val=0;
for (i=ed*block_len;i<r;i++)
aa[i]+=x;
tm[ed].num=0;
len=min( ed*block_len+block_len ,n); //一定要限制右端点
for (i=ed*block_len;i<len;i++)
tm[ed].xx[++tm[ed].num]=node(aa[i] ,i);
sort(tm[ed].xx+1,tm[ed].xx+1+tm[ed].num,sort_cmp);
// tm[ed].val=0;
for (i=st+1;i<=ed-1;i++)
tm[i].val+=x;
}
}
else
{
__int64 y;
scanf("%I64d",&y);
__int64 ll=-1;
__int64 rr=-1;
for (i=0;i<sg_num;i++)
{
__int64 it=lower_bound(tm[i].xx+1,tm[i].xx+1+tm[i].num,y-tm[i].val,cmp)-tm[i].xx;
if (it==tm[i].num+1) continue; //不存在该元素
else
{
if (tm[i].xx[it].x==y-tm[i].val) {ll=tm[i].xx[it].id;break;}
else continue;
}
}
for (i=sg_num-1;i>=0;i--)
{
__int64 it=upper_bound(tm[i].xx+1,tm[i].xx+1+tm[i].num,y-tm[i].val,cmp)-tm[i].xx;
it--;
if (it==0) continue; //不存在该元素y
if (tm[i].xx[it].x==y-tm[i].val) {rr=tm[i].xx[it].id;break;}
else continue;
}
if (ll==-1)
printf("-1\n");
else
printf("%I64d\n",rr-ll);
}
}
return 0;
}