E. GukiZ and GukiZiana
time limit per test:10 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output
Professor GukiZ was playing with arrays again and accidentally discovered new function, which he calledGukiZiana. For given array a, indexed with integers from 1 to n, and number y, GukiZiana(a, y) represents maximum value of j - i, such that aj = ai = y. If there is no y as an element in a, then GukiZiana(a, y) is equal to - 1. GukiZ also prepared a problem for you. This time, you have two types of queries:
- First type has form 1lrxand asks you to increase values of allaisuch thatl≤i≤rby the non-negative integerx.
- Second type has form 2yand asks you to find value ofGukiZiana(a,y).
For each query of type 2, print the answer and make GukiZ happy!
Input
The first line contains two integers n,q (1 ≤ n ≤ 5 * 105, 1 ≤ q ≤ 5 * 104), size of array a, and the number of queries.
The second line contains n integers a1, a2, ...an (1 ≤ ai ≤ 109), forming an array a.
Each of next q
If line starts with 1, then the query looks like1 l r x (1 ≤ l ≤ r ≤ n,0 ≤ x ≤ 109), first type query.
If line starts with 2, then th query looks like 2 y (1 ≤ y ≤ 109), second type query.
Output
For each query of type 2, print the value of GukiZiana(a, y), for y
Examples
Input
4 3 1 2 3 4 1 1 2 1 1 1 1 1 2 3
Output
2
Input
2 3 1 2 1 2 2 1 2 3 2 4
Output
0 -1
事实证明,有些时候分块水不一定真的是水,它的水反而可以解决一些数据结构处理不了的问题。
这道题很明显的一道数据结构,大致题意:两种操作,一是区间修改,二是查询整个区间中等于某一个数字的值出现的最大位置差,如果不存在则输出-1。
第一个操作显然用线段树可以实现,然后关于第二个操作,我想起来之前有一道题目,转换到最后是找区间中第一个小于等于某一个数字x的位置。当时的话也是利用二分的思想。对于一个区间,我判断其最小值是否小于x,如果小于,那么说明答案可以在该区间内,又由于要是第一个位置,所以我继续在该区间的左子区间去寻找,直到找到一个叶子节点。非常巧妙的方法,复杂度为O(NlogN)。然后我一开始也想模仿此题的思路,然而结果确实TLE。经过仔细思考,道理也很明显,如果按照这种方法,如果某个区间的最大和最小值区间包含了数字x,那么这个区间就可能是答案所在区间,但是很显然也可以不是。这样子的话,有可能我把整个区间都找一遍之后还是找不到恰好等于x的数字,坏就坏在要求恰好等于。因此,这个复杂度可以退化到O(N^2)甚至更坏(在树形存储下暴力可能会比普通暴力更慢)。
于是不得不重新思考做题方法。通过常识,我们知道,对于一个无序的区间,我们无法在动态修改的情况下做到均摊的O(1)判定一个数字否存在于这个区间,那么log的复杂度可能吗?但是log级别的二分查找需要进行排序,那么为什么不排序呢?
整体的排序没有必要且消耗时间,所以我们可以用分块思想,分成很多个小的区间,然后在小的区间内进行排序。对于修改操作,按照套路可以分为在块内部分的修改和块整体的修改。我们发现,块整体修改的时候,相当于块内每个数字同时增加,这对块内数字相互的大小关系并没有影响,所以这种情况就没有必要进行重新排序。当块内部分修改的时候,大小关系可能改变,所以我们干脆重新排序。由于分了块,所以即使重新排序,这个复杂度也不算太高,总的来说,单次修改的复杂度是O((N^0.5)log(N^0.5))。
然后就是查询操作,由于分了块,然后块内有序,所以说我们可以用二分查找去逐个判定每一个块内是否存在这样一个恰好等于x的数字。确定了左右第一个存在x的块之后,我再在原块所在区间内(没有排序的区间)暴力寻找这个x的具体位置即可。可以看见,查询的复杂度也是O((N^0.5)log(N^0.5))。算上初始排序的复杂度,总的时间复杂度就是
。需要注意的问题是,数组的数值要使用LL类型,然后在查询的时候别忘了lazy的影响,在查询之前首先把x减去所在块的lazy值,然后再进行查找。具体见代码:
#include<bits/stdc++.h>
#define LL long long
#define N 501000
#define M 1000
using namespace std;
LL a[N],num[M][M],lazy[M];
int n,q,lpos,rpos,blocks;
bool flag;
int main()
{
scanf("%d%d",&n,&q);
blocks=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num[i/blocks][i%blocks]=a[i];
}
for(int i=0;i<=n/blocks;i++)
sort(num[i],num[i]+blocks);
while(q--)
{
int op;
scanf("%d",&op);
if (op==1)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
if (l/blocks==r/blocks) //判断是否区间在一个块内部
{
int No=l/blocks;
for(int i=l;i<=r;i++) a[i]+=x; //暴力修改数值
for(int i=No*blocks;i<(No+1)*blocks;i++) num[No][i%blocks]=a[i]; //赋值后重新排序
sort(num[No],num[No]+blocks);
} else
{
int L=l/blocks,R=r/blocks;
for(int i=L+1;i<R;i++) lazy[i]+=x; //跨越整个块,对数值lazy标记
for(int i=R*blocks;i<=r;i++) a[i]+=x; //块内暴力修改
for(int i=l;i<(L+1)*blocks;i++) a[i]+=x;
for(int i=L*blocks;i<(L+1)*blocks;i++) num[L][i%blocks]=a[i]; //块内赋值重新排序
for(int i=R*blocks;i<(R+1)*blocks;i++) num[R][i%blocks]=a[i];
sort(num[L],num[L]+blocks); sort(num[R],num[R]+blocks);
}
} else
{
LL y;
lpos=rpos=-1;
scanf("%I64d",&y);
for(int i=0;i<=n/blocks;i++) //找左边第一个出现
{
int pos=lower_bound(num[i],num[i]+blocks,y-lazy[i])-num[i]; //判断当前块内是否有y,注意lazy的影响
if (num[i][pos]==y-lazy[i]) {lpos=i;break;}
}
if (lpos==-1) {puts("-1");continue;}
for(int i=lpos*blocks;i<(lpos+1)*blocks;i++) //在块所确定的区间内,找最左边的y的实际出现位置
if (a[i]==y-lazy[lpos]) {lpos=i;break;}
for(int i=n/blocks;i>=0;i--) //找右边第一个出现的y,与左边同理,不过是从右往左找
{
int pos=lower_bound(num[i],num[i]+blocks,y-lazy[i])-num[i];
if (num[i][pos]==y-lazy[i]) {rpos=i;break;}
}
for(int i=(rpos+1)*blocks-1;i>=rpos*blocks;i--)
if (a[i]==y-lazy[rpos]) {rpos=i;break;}
printf("%d\n",rpos-lpos);
}
}
return 0;
}