数列分块入门1 – 9 by hzwer
本文非原创,原创地址http://hzwer.com/8053.html
- 可能涉及的几个词语解释:
- 区间:数列中连续一段的元素
- 区间操作:将某个区间[a,b]的所有元素进行某种改动的操作
- 块:我们将数列划分成若干个不相交的区间,每个区间称为一个块
- 整块:在一个区间操作时,完整包含于区间的块
- 不完整的块:在一个区间操作时,只有部分包含于区间的块,即区间左右端点所在的两个块
分块算法是一种很常见的根号算法,一般它的时间复杂度会带根号。
分块和线段树的区别在于,分块算法可以维护一些线段树维护不了的东西,例如单调队列等,线段树能维护的东西必须能够进行信息合并,而分块则不需要。不过,它们也有共同点,分块和线段树一样,分块需要支持类似标记合并的东西。
简单来说,分块算法就是优化过后的暴力。
现在讲一下这种算法的实现。
这种算法会将序列(序列长度为
N
N
)进行分块,通常设置一个上限 ,每一块有至多
K
K
个元素。在序列分块问题上,一般会严格要求每个块都要有 个元素,这样就会分成约
N/K
N
/
K
块。(最后一个块除外)
我们一般都会设
K=N−−√
K
=
N
,这样块数也就只有
N/K=N−−√
N
/
K
=
N
块数可能多一。
通常实现时,我们用be i bei表示第i个位置所属的块。对于每个块都进行信息维护。
单点修改时,我们一般先将对应块的标记下传,再暴力更新被修改块的状态。
时间复杂度
ON−−√)
O
N
)
。
如果是区间
[L,R]
[
L
,
R
]
修改的话,对于被
[L,R]
[
L
,
R
]
整块跨过的块直接打标记,两端剩余的部分暴力重构块的状态即可。
中间最多经过
NN−−√
N
N
块,两边暴力修改也是
NN−−√
N
N
次的,所以时间复杂度为
O(N−−√)
O
(
N
)
。
至于询问操作,和区间修改类似,对于中间跨过的整块,直接利用块保存的信息统计答案,两端剩余部分任然可以暴力扫描统计。
时间复杂度和区间修改一样,也是
O(N−−√)
O
(
N
)
。
如果询问次数为m m,那总的时间复杂度即为 O(MN−−√) O ( M N ) 。
分块入门 1 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
这是一道能用许多数据结构优化的经典题,可以用于不同数据结构训练。
数列分块就是把数列中每m个元素打包起来,达到优化算法的目的。
以此题为例,如果我们把每 m m 个元素分为一块,共有块,每次区间加的操作会涉及 O(n/m) O ( n / m ) 个整块,以及区间两侧两个不完整的块中至多 2m 2 m 个元素。
我们给每个块设置一个加法标记(就是记录这个块中元素一起加了多少),每次操作对每个整块直接 O(1) O ( 1 ) 标记,而不完整的块由于元素比较少,暴力修改元素的值。
每次询问时返回元素的值加上其所在块的加法标记。
这样每次操作的复杂度是 O(n/m)+O(m) O ( n / m ) + O ( m ) ,根据均值不等式,当 m m 取时总复杂度最低,为了方便,我们都默认下文的分块大小为 n−−√ n 。
分块入门 2 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
有了上一题的经验,我们可以发现,数列简单分块问题实际上有三项东西要我们思考:
对于每次区间操作:
1.不完整的块 的 N−−√ N 个元素怎么处理?
2. N−−√ N 个 整块 怎么处理?
3.要预处理什么信息(复杂度不能超过后面的操作)?
我们先来思考只有询问操作的情况,不完整的块枚举统计即可;而要在每个整块内寻找小于一个值的元素数,于是我们不得不要求块内元素是有序的,这样就能使用二分法对块内查询,需要预处理时每块做一遍排序,复杂度O(nlogn),每次查询在√n个块内二分,以及暴力 2N−−√ 2 N 个元素,总复杂度 O(nlogn+nn−−√logn−−√) O ( n l o g n + n n l o g n ) 。
可以通过均值不等式计算出更优的分块大小,就不展开讨论了
那么区间加怎么办呢?
套用第一题的方法,维护一个加法标记,略有区别的地方在于,不完整的块修改后可能会使得该块内数字乱序,所以头尾两个不完整块需要重新排序,复杂度分析略。
在加法标记下的询问操作,块外还是暴力,查询小于(x – 加法标记)的元素个数,块内用(x – 加法标记)作为二分的值即可。
分块入门 3 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。
n<=100000其实是为了区分暴力和一些常数较大的写法。
接着第二题的解法,其实只要把块内查询的二分稍作修改即可。
不过这题其实想表达:可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。
分块的调试检测技巧:
可以生成一些大数据,然后用两份分块大小不同的代码来对拍,还可以根据运行时间尝试调整分块大小,减小常数。
分块入门 4 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间加法,区间求和。
这题的询问变成了区间上的询问,不完整的块还是暴力;而要想快速统计完整块的答案,需要维护每个块的元素和,先要预处理一下。
考虑区间修改操作,不完整的块直接改,顺便更新块的元素和;完整的块类似之前标记的做法,直接根据块的元素和所加的值计算元素和的增量。
分块入门 5 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。
稍作思考可以发现,开方操作比较棘手,主要是对于整块开方时,必须要知道每一个元素,才能知道他们开方后的和,也就是说,难以快速对一个块信息进行更新。
看来我们要另辟蹊径。不难发现,这题的修改就只有下取整开方,而一个数经过几次开方之后,它的值就会变成 0 或者 1。
如果每次区间开方只不涉及完整的块,意味着不超过2√n个元素,直接暴力即可。
如果涉及了一些完整的块,这些块经过几次操作以后就会都变成 0 / 1,于是我们采取一种分块优化的暴力做法,只要每个整块暴力开方后,记录一下元素是否都变成了 0 / 1,区间修改时跳过那些全为 0 / 1 的块即可。
这样每个元素至多被开方不超过4次,显然复杂度没有问题。
分块入门 6 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。
先说随机数据的情况
之前提到过,如果我们块内用数组以外的数据结构,能够支持其它不一样的操作,比如此题每块内可以放一个动态的数组,每次插入时先找到位置所在的块,再暴力插入,把块内的其它元素直接向后移动一位,当然用链表也是可以的。
查询的时候类似,复杂度分析略。
但是这样做有个问题,如果数据不随机怎么办?
如果先在一个块有大量单点插入,这个块的大小会大大超过√n,那块内的暴力就没有复杂度保证了。
还需要引入一个操作:重新分块(重构)
每根号n次插入后,重新把数列平均分一下块,重构需要的复杂度为O(n),重构的次数为√n,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。
当然,也可以当某个块过大时重构,或者只把这个块分成两半。
分块入门 7 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间乘法,区间加法,单点询问。
很显然,如果只有区间乘法,和分块入门 1 的做法没有本质区别,但要思考如何同时维护两种标记。
我们让乘法标记的优先级高于加法(如果反过来的话,新的加法标记无法处理)
若当前的一个块乘以m1后加上a1,这时进行一个乘m2的操作,则原来的标记变成m1*m2,a1*m2
若当前的一个块乘以m1后加上a1,这时进行一个加a2的操作,则原来的标记变成m1,a1+a2
分块入门 8 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及区间询问等于一个数c的元素,并将这个区间的所有元素改为c。
区间修改没有什么难度,这题难在区间查询比较奇怪,因为权值种类比较多,似乎没有什么好的维护方法。
模拟一些数据可以发现,询问后一整段都会被修改,几次询问后数列可能只剩下几段不同的区间了。
我们思考这样一个暴力,还是分块,维护每个分块是否只有一种权值,区间操作的时候,对于同权值的一个块就O(1)统计答案,否则暴力统计答案,并修改标记,不完整的块也暴力。
这样看似最差情况每次都会耗费O(n)的时间,但其实可以这样分析:
假设初始序列都是同一个值,那么查询是O(√n),如果这时进行一个区间操作,它最多破坏首尾2个块的标记,所以只能使后面的询问至多多2个块的暴力时间,所以均摊每次操作复杂度还是O(√n)。
换句话说,要想让一个操作耗费O(n)的时间,要先花费√n个操作对数列进行修改。
初始序列不同值,经过类似分析后,就可以放心的暴力啦。
分块入门 9 by hzwer
传送门
给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。
这是一道经典难题,其实可以支持修改操作,具体见陈立杰大神的区间众数解题报告。
而且不强制在线的话有很多做法,可以看我blog一道类似题目 czy的后宫3
bzoj2724 是道强制在线区间众数,而且题目背景写的不错,这道题的题解就贴传送门咯
「bzoj2724」[Violet 6]蒲公英
分块入门1:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo;
int v[50005],bl[50005],atag[50005];
void add(int a,int b,int c)
{
for(int i=a;i<=min(bl[a]*blo,b);i++)
v[i]+=c;
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
v[i]+=c;
for(int i=bl[a]+1;i<=bl[b]-1;i++)
atag[i]+=c;
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1;
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)add(a,b,c);
if(f==1)printf("%d\n",v[b]+atag[bl[b]]);
}
return 0;
}
分块入门2:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo;
int v[50005],bl[50005],atag[50005];
vector<int>ve[505];
void reset(int x)
{
ve[x].clear();
for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
ve[x].push_back(v[i]);
sort(ve[x].begin(),ve[x].end());
}
void add(int a,int b,int c)
{
for(int i=a;i<=min(bl[a]*blo,b);i++)
v[i]+=c;
reset(bl[a]);
if(bl[a]!=bl[b])
{
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
v[i]+=c;
reset(bl[b]);
}
for(int i=bl[a]+1;i<=bl[b]-1;i++)
atag[i]+=c;
}
int query(int a,int b,int c)
{
int ans=0;
for(int i=a;i<=min(bl[a]*blo,b);i++)
if(v[i]+atag[bl[a]]<c)ans++;
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
if(v[i]+atag[bl[b]]<c)ans++;
for(int i=bl[a]+1;i<=bl[b]-1;i++)
{
int x=c-atag[i];
ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin();
}
return ans;
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
{
bl[i]=(i-1)/blo+1;
ve[bl[i]].push_back(v[i]);
}
for(int i=1;i<=bl[n];i++)
sort(ve[i].begin(),ve[i].end());
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)add(a,b,c);
if(f==1)printf("%d\n",query(a,b,c*c));
}
return 0;
}
分块入门3:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo;
int v[100005],bl[100005],atag[100005];
set<int>st[105];
void add(int a,int b,int c)
{
for(int i=a;i<=min(bl[a]*blo,b);i++)
{
st[bl[a]].erase(v[i]);
v[i]+=c;
st[bl[a]].insert(v[i]);
}
if(bl[a]!=bl[b])
{
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
{
st[bl[b]].erase(v[i]);
v[i]+=c;
st[bl[b]].insert(v[i]);
}
}
for(int i=bl[a]+1;i<=bl[b]-1;i++)
atag[i]+=c;
}
int query(int a,int b,int c)
{
int ans=-1;
for(int i=a;i<=min(bl[a]*blo,b);i++)
{
int val=v[i]+atag[bl[a]];
if(val<c)ans=max(val,ans);
}
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
{
int val=v[i]+atag[bl[b]];
if(val<c)ans=max(val,ans);
}
for(int i=bl[a]+1;i<=bl[b]-1;i++)
{
int x=c-atag[i];
set<int>::iterator it=st[i].lower_bound(x);
if(it==st[i].begin())continue;
--it;
ans=max(ans,*it+atag[i]);
}
return ans;
}
int main()
{
n=read();blo=1000;
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
{
bl[i]=(i-1)/blo+1;
st[bl[i]].insert(v[i]);
}
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)add(a,b,c);
if(f==1)printf("%d\n",query(a,b,c));
}
return 0;
}
分块入门4:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo;
int bl[50005];
ll v[50005],atag[50005],sum[50005];
void add(int a,int b,int c)
{
for(int i=a;i<=min(bl[a]*blo,b);i++)
v[i]+=c,sum[bl[a]]+=c;;
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
v[i]+=c,sum[bl[b]]+=c;
for(int i=bl[a]+1;i<=bl[b]-1;i++)
atag[i]+=c;
}
ll query(int a,int b)
{
ll ans=0;
for(int i=a;i<=min(bl[a]*blo,b);i++)
ans+=v[i]+atag[bl[a]];
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
ans+=v[i]+atag[bl[b]];
for(int i=bl[a]+1;i<=bl[b]-1;i++)
ans+=sum[i]+blo*atag[i];
return ans;
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
{
bl[i]=(i-1)/blo+1;
sum[bl[i]]+=v[i];
}
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)add(a,b,c);
if(f==1)
printf("%d\n",query(a,b)%(c+1));
}
return 0;
}
分块入门5:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo;
int bl[50005];
int v[50005],sum[50005],flag[50005];
void solve_sqrt(int x)
{
if(flag[x])return;
flag[x]=1;
sum[x]=0;
for(int i=(x-1)*blo+1;i<=x*blo;i++)
{
v[i]=sqrt(v[i]),sum[x]+=v[i];
if(v[i]>1)flag[x]=0;
}
}
void add(int a,int b,int c)
{
for(int i=a;i<=min(bl[a]*blo,b);i++)
{
sum[bl[a]]-=v[i];
v[i]=sqrt(v[i]);
sum[bl[a]]+=v[i];
}
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
{
sum[bl[b]]-=v[i];
v[i]=sqrt(v[i]);
sum[bl[b]]+=v[i];
}
for(int i=bl[a]+1;i<=bl[b]-1;i++)
solve_sqrt(i);
}
int query(int a,int b)
{
int ans=0;
for(int i=a;i<=min(bl[a]*blo,b);i++)
ans+=v[i];
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
ans+=v[i];
for(int i=bl[a]+1;i<=bl[b]-1;i++)
ans+=sum[i];
return ans;
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
{
bl[i]=(i-1)/blo+1;
sum[bl[i]]+=v[i];
}
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)add(a,b,c);
if(f==1)
printf("%d\n",query(a,b));
}
return 0;
}
分块入门6:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo,m;
int v[100005];
vector<int>ve[1005];
int st[200005],top;
pair<int,int> query(int b)
{
int x=1;
while(b>ve[x].size())
b-=ve[x].size(),x++;
return make_pair(x,b-1);
}
void rebuild()
{
top=0;
for(int i=1;i<=m;i++)
{
for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++)
st[++top]=*j;
ve[i].clear();
}
int blo2=sqrt(top);
for(int i=1;i<=top;i++)
ve[(i-1)/blo2+1].push_back(st[i]);
m=(top-1)/blo2+1;
}
void insert(int a,int b)
{
pair<int,int> t=query(a);
ve[t.first].insert(ve[t.first].begin()+t.second,b);
if(ve[t.first].size()>20*blo)
rebuild();
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
ve[(i-1)/blo+1].push_back(v[i]);
m=(n-1)/blo+1;
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)insert(a,b);
if(f==1)
{
pair<int,int> t=query(b);
printf("%d\n",ve[t.first][t.second]);
}
}
return 0;
}
分块入门7:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo,m;
int v[100005];
vector<int>ve[1005];
int st[200005],top;
pair<int,int> query(int b)
{
int x=1;
while(b>ve[x].size())
b-=ve[x].size(),x++;
return make_pair(x,b-1);
}
void rebuild()
{
top=0;
for(int i=1;i<=m;i++)
{
for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++)
st[++top]=*j;
ve[i].clear();
}
int blo2=sqrt(top);
for(int i=1;i<=top;i++)
ve[(i-1)/blo2+1].push_back(st[i]);
m=(top-1)/blo2+1;
}
void insert(int a,int b)
{
pair<int,int> t=query(a);
ve[t.first].insert(ve[t.first].begin()+t.second,b);
if(ve[t.first].size()>20*blo)
rebuild();
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
ve[(i-1)/blo+1].push_back(v[i]);
m=(n-1)/blo+1;
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)insert(a,b);
if(f==1)
{
pair<int,int> t=query(b);
printf("%d\n",ve[t.first][t.second]);
}
}
return 0;
}
分块入门8:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo,m;
int v[100005];
vector<int>ve[1005];
int st[200005],top;
pair<int,int> query(int b)
{
int x=1;
while(b>ve[x].size())
b-=ve[x].size(),x++;
return make_pair(x,b-1);
}
void rebuild()
{
top=0;
for(int i=1;i<=m;i++)
{
for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++)
st[++top]=*j;
ve[i].clear();
}
int blo2=sqrt(top);
for(int i=1;i<=top;i++)
ve[(i-1)/blo2+1].push_back(st[i]);
m=(top-1)/blo2+1;
}
void insert(int a,int b)
{
pair<int,int> t=query(a);
ve[t.first].insert(ve[t.first].begin()+t.second,b);
if(ve[t.first].size()>20*blo)
rebuild();
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
ve[(i-1)/blo+1].push_back(v[i]);
m=(n-1)/blo+1;
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)insert(a,b);
if(f==1)
{
pair<int,int> t=query(b);
printf("%d\n",ve[t.first][t.second]);
}
}
return 0;
}
分块入门9:
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo,m;
int v[100005];
vector<int>ve[1005];
int st[200005],top;
pair<int,int> query(int b)
{
int x=1;
while(b>ve[x].size())
b-=ve[x].size(),x++;
return make_pair(x,b-1);
}
void rebuild()
{
top=0;
for(int i=1;i<=m;i++)
{
for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++)
st[++top]=*j;
ve[i].clear();
}
int blo2=sqrt(top);
for(int i=1;i<=top;i++)
ve[(i-1)/blo2+1].push_back(st[i]);
m=(top-1)/blo2+1;
}
void insert(int a,int b)
{
pair<int,int> t=query(a);
ve[t.first].insert(ve[t.first].begin()+t.second,b);
if(ve[t.first].size()>20*blo)
rebuild();
}
int main()
{
n=read();blo=sqrt(n);
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++)
ve[(i-1)/blo+1].push_back(v[i]);
m=(n-1)/blo+1;
for(int i=1;i<=n;i++)
{
int f=read(),a=read(),b=read(),c=read();
if(f==0)insert(a,b);
if(f==1)
{
pair<int,int> t=query(b);
printf("%d\n",ve[t.first][t.second]);
}
}
return 0;
}