数列分块入门1 – 9 by hzwer

数列分块入门1 – 9 by hzwer

本文非原创,原创地址http://hzwer.com/8053.html
- 可能涉及的几个词语解释:

  • 区间:数列中连续一段的元素
  • 区间操作:将某个区间[a,b]的所有元素进行某种改动的操作
  • 块:我们将数列划分成若干个不相交的区间,每个区间称为一个块
  • 整块:在一个区间操作时,完整包含于区间的块
  • 不完整的块:在一个区间操作时,只有部分包含于区间的块,即区间左右端点所在的两个块

分块算法是一种很常见的根号算法,一般它的时间复杂度会带根号。
分块和线段树的区别在于,分块算法可以维护一些线段树维护不了的东西,例如单调队列等,线段树能维护的东西必须能够进行信息合并,而分块则不需要。不过,它们也有共同点,分块和线段树一样,分块需要支持类似标记合并的东西。
简单来说,分块算法就是优化过后的暴力。

现在讲一下这种算法的实现。
这种算法会将序列(序列长度为 N N )进行分块,通常设置一个上限K ,每一块有至多 K 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 个元素分为一块,共有n/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 n

分块入门 2 by hzwer

传送门
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。

有了上一题的经验,我们可以发现,数列简单分块问题实际上有三项东西要我们思考:

对于每次区间操作:

1.不完整的块 的 N N 个元素怎么处理?

2. N N 个 整块 怎么处理?

3.要预处理什么信息(复杂度不能超过后面的操作)?

我们先来思考只有询问操作的情况,不完整的块枚举统计即可;而要在每个整块内寻找小于一个值的元素数,于是我们不得不要求块内元素是有序的,这样就能使用二分法对块内查询,需要预处理时每块做一遍排序,复杂度O(nlogn),每次查询在√n个块内二分,以及暴力 2N 2 N 个元素,总复杂度 O(nlogn+nnlogn) 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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值