CodeForces - 551E 二分查找

题目链接
题目大意
给一个数量级为5*10^5的long long数组,进行5*10^4次操作,操作分为两种:
1、令序号在【L,R】区间的数值增加x
2、找到数组内等于y值的两个数的最大距离
主要思想
分块+二分查找
每sqrt(n)个数分一组
代码块:
# include <bits/stdc++.h>
using namespace std;
const int MAXN=5*1e5+10;
int n, q;
long long a[MAXN], b[MAXN];
int block, t;///每个数据块的个数及数据块的总量
int L[MAXN], R[MAXN];///记录每个分区间的左右序号
long long add[MAXN];
void Init(int data)
{
for(int i=L[data];i<=R[data];i++)
b[i]=a[i];///b数组用于排序后查询此区间是否有所要查找的key值
sort(b+L[data],b+R[data]+1);
}
void change(int l, int r, long long x)/*首先看要修改的区间是否在一个数据块中,如果在暴力维护;
否则先暴力维护左右两边不完整的数据块,其次维护中间完整的数据块,把每块数据所要增加的值记录到
add数组中;(需要同时维护a、b两个数组,并对b数组进行排序)*/
{
int numl=(l-1)/block+1;
int numr=(r-1)/block+1;
if(numl==numr)///如果l、r位于同一个数据块,暴力维护整个区间
{
for(int i=l;i<=r;i++)
a[i]+=x;
Init(numl);
return;
}
if(l==L[numl])///如果要修改的区间的左边界刚好等于整个区间所在数据块的左数据块的左边界
l=numl;
else///否则维护左边不完整的数据块
{
for(int i=l;i<=R[numl];i++)
a[i]+=x;
Init(numl);
l=numl+1;
}
if(r==R[numr])
r=numr;
else
{
for(int i=L[numr];i<=r;i++)
a[i]+=x;
Init(numr);
r=numr-1;
}
for(int i=l;i<=r;i++)
add[i]+=x;
}
int Find(int l, int r, long long x)///二分查找,干货
{
while(l<=r)
{
int mid=(l+r)/2;
if(b[mid]==x)
return mid;
else if(b[mid]>x)
r=mid-1;
else
l=mid+1;
}
return -1;
}
int query(long long y)
{
int ansl=n;
int ansr=0;
for(int i=1;i<=t;i++)///遍历所有数据块
{
int loc=Find(L[i],R[i],y-add[i]);///如果在当前数据块中找到key值,暴力遍历a数组当前数据块找到其具体位置
if(loc==-1)
continue;
for(int j=L[i];j<=R[i];j++)
{
if(a[j]+add[i]==y)
{
ansl=j;
break;
}
}
break;///按照数据块序号查找,找到即退出,找到最左边的key值所在序号
}
for(int i=t;i>=1;i--)///从右往左找
{
int loc=Find(L[i],R[i],y-add[i]);
if(loc==-1)
continue;
for(int j=R[i];j>=L[i];j--)
{
if(a[j]+add[i]==y)
{
ansr=j;
break;
}
}
break;
}
if(ansl>ansr)return -1;
return ansr-ansl;
}
int main()
{
while(cin>>n>>q)
{
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]);
block=sqrt(n);///每个数据块的数据个数
t=(n-1)/block+1;///数据块的数量
L[1]=1;
R[1]=block;
for(int i=2;i<=t;i++)
{
L[i]=L[i-1]+block;
R[i]=R[i-1]+block;
}
R[t]=n;
for(int i=1;i<=t;i++)
Init(i);
for(int i=1;i<=q;i++)
{
int data;
scanf("%d",&data);
if(data==1)
{
int a, b;
long long c;
scanf("%d%d%I64d",&a,&b,&c);///输入long long类型数据采用I64d形式
change(a,b,c);///维护存放数据的区间
}
else if(data==2)
{
long long key;
scanf("%I64d",&key);
cout<<query(key)<<endl;
}
}
}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值