#6282. 数列分块入门 6

Posted caijiaming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#6282. 数列分块入门 6相关的知识,希望对你有一定的参考价值。

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

题目描述

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

输入格式

第一行输入一个数字 nn。

第二行输入 nn 个数字,第 ii 个数字为 a_iai?,以空格隔开。

接下来输入 nn 行询问,每行输入四个数字 mathrm{opt}opt、ll、rr、cc,以空格隔开。

若 mathrm{opt} = 0opt=0,表示在第 ll 个数字前插入数字 rr(cc 忽略)。

若 mathrm{opt} = 1opt=1,表示询问 a_rar? 的值(ll 和 cc 忽略)。

输出格式

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

样例

样例输入

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

样例输出

2
3

数据范围与提示

对于 100\%100% 的数据,1 leq n leq 100000, -2^{31} leq mathrm{others}1n100000,?231others、mathrm{ans} leq 2^{31}-1ans231?1。

 

思路:这题主要 要解决的问题是插入数据的问题,可以想到,当插入数据的时候,对应块的大小就改变了,那么我们怎么找第r个位置的数呢?  很简单,可以暴力从第一个块开始找,一直减去每个块的

大小,知道r<块的大小时,说明r位于这个块,并且在块中的位置就是先在的r,  本题使用vector来做,动态数组,因为插入多少个数据是不知道的,应用动态数组是必然,正好vector中也有Insert(it,c)函数

在it位置插入数据c   其他的元素后移,这就很方便了。 下面是一种超时的代码:

#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
const int maxn=100000+5;
int a[maxn];
vector<int> v[maxn];
int block;
pair<int,int> Query(int x)
{
    int cnt=1;
    while(x>v[cnt].size())
    {
        x-=v[cnt].size();
        cnt++;
    }
    return make_pair(cnt,x-1);//哪个块里面的第几个元素
}
void Updata(int l,int r)
{
    pair<int,int> t=Query(l);
    v[t.first].insert(v[t.first].begin()+t.second,r);//找到相应的块 并在对应位置插入r
    //重构
}
int main()
{
    int n;
    int opt,l,r,c;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        v[(i-1)/block+1].push_back(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        cin>>opt>>l>>r>>c;
        if(opt==0) Updata(l,r);
        else
        {
            pair<int,int>t=Query(r);
            cout<<v[t.first][t.second]<<endl;
        }
    }
}

 

 

为什么会超时呢?   当我们插入数据一直在一个块的时候,就会导致某个块的大小很大很大,这就使得访问的时候时间复杂度变的很大了,所以下面增加另外一个操作,重构!

有两种分块的方法  :  当根号n  次插入时  重新分块 这样时间复杂度最多也就根号n    *   n     也可以当某个块的大小很大时分块  只要能保证足够就行了

下面看代码:

#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
const int maxn=100000+5;
int a[maxn];
vector<int> v[maxn];
int st[2*maxn];
int block;
int m;
int top;
int sum=0;
pair<int,int> Query(int x)
{
    int cnt=1;
    while(x>v[cnt].size())
    {
        x-=v[cnt].size();
        cnt++;
    }
    return make_pair(cnt,x-1);//哪个块里面的第几个元素
}
void Rebuild()
{
    sum=0;
    top=0;
    for(int i=1;i<=m;i++)
    {
        for(vector<int>::iterator it=v[i].begin();it!=v[i].end();it++)
        {
            st[++top]=*it;
        }
        v[i].clear();
    }
    int block2=sqrt(top);
    for(int i=1;i<=top;i++)
    {
        v[(i-1)/block2+1].push_back(st[i]);
    }
    m=(top-1)/block2+1;
}
void Updata(int l,int r)
{
    sum++;
    pair<int,int> t=Query(l);
    v[t.first].insert(v[t.first].begin()+t.second,r);//找到相应的块 并在对应位置插入r
    //重构
    if(sum==block)
        Rebuild();
}
int main()
{
    int n;
    int opt,l,r,c;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    block=sqrt(n);
    m=(n-1)/block+1;
    for(int i=1;i<=n;i++)
    {
        v[(i-1)/block+1].push_back(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        cin>>opt>>l>>r>>c;
        if(opt==0) Updata(l,r);
        else
        {
            pair<int,int>t=Query(r);
            cout<<v[t.first][t.second]<<endl;
        }
    }
}

 

以上是关于#6282. 数列分块入门 6的主要内容,如果未能解决你的问题,请参考以下文章

#6282. 数列分块入门 6

LOJ.6282.数列分块入门6(块状链表/分块)

LibreOJ 6282. 数列分块入门 6

LOJ#6282. 数列分块入门 6

Loj 6282. 数列分块入门 6

数列分块入门6 解题报告