#6282. 数列分块入门 6

题目链接:https://loj.ac/problem/6282

题目描述

给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。

输入格式

第一行输入一个数字 n

第二行输入 n 个数字,第 i 个数字为 ai,以空格隔开。

接下来输入 n 行询问,每行输入四个数字 opt、lrc,以空格隔开。

若 opt=0,表示在第 l 个数字前插入数字 r (c 忽略)。

若 opt=1,表示询问 ar 的值(l 和 c 忽略)。

输出格式

对于每次询问,输出一行一个数字表示答案。

样例

样例输入

4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4

样例输出

2
3

思路:每一个分块的数据用vector来维护,这样便于实现插入操作,并且往后挪移一位元素的复杂也不会太高。

如果插入元素太多的话,需要重新分块,因为不重新分块,可能每次都查询这个块,会超时的。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int a[2*maxn],n,block,maxpos;
vector<int>v[1010];

pair<int,int> query(int l)//查找第l个元素的位置 
{
    int cnt=1;
    while(l>v[cnt].size())
    {
        l-=v[cnt].size();
        cnt++;
    }
    return make_pair(cnt,l-1);
}

void rebuild()//重新分块 
{
    int cnt=0;
    for(int i=1;i<=maxpos;i++)
    {
        for(int j=0;j<v[i].size();j++)
            a[++cnt]=v[i][j];
        v[i].clear();
    }
    block=sqrt(cnt);//可要可不要,要即是用新的块大小,不要block变化不大,没有关系 
    for(int i=1;i<=cnt;i++)
    {
        v[(i-1)/block+1].push_back(a[i]);
    }
    maxpos=(cnt-1)/block+1;
}

void insert(int l,int r)//插入 
{
    pair<int,int> w=query(l);//找到插入的位置 
    v[w.first].insert(v[w.first].begin()+w.second,r);//插入进去 
    if(v[w.first].size()>10*block)//如果插入太多,导致块变得很大,需要重新分块 
        rebuild();
}

int find(int l,int r)
{
    pair<int ,int >w=query(r);
    return v[w.first][w.second];
}

int main()
{
    scanf("%d",&n);
    block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        v[(i-1)/block+1].push_back(x);
    }
    maxpos=(n-1)/block+1;//最后的块 
    int opt,l,r,c;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d%d%d",&opt,&l,&r,&c);
        if(opt==0)
            insert(l,r);
        else
            printf("%d
",find(l,r));
    }
}
 
原文地址:https://www.cnblogs.com/xiongtao/p/9792422.html