数列分块入门 6
链接:LibreOj6282
题目描述
给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问。
输入格式
第一行输入一个数字n。
第二行输入n个数字,第i个数字为 a[i],以空格隔开。
接下来输入n行询问,每行输入四个数字op、l、r、c,以空格隔开。
若 op==0,表示在第l个数字前插入数字r( c忽略)。
若 op==1,表示询问a[r]的值( 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(或者其他?)分开存放每个块得数据。
这样得好处是每个块可以使用stl得很多操作。就比如这题的插入。
但是一个块如果一直插入数据,块的数据量会一直增大。
所以在块的数据多大的时候(比如大于三个block大小),我们就重新分块。
这样能保证之后的操作耗时不会太大。
ps:不过这题不重新分块也能过(但是会花两倍多时间)。
完整代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<string>
#include<algorithm>
#include<sstream>
#include<memory>
#include<utility>
#include<functional>
#include<iterator>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=1e5+5;
int belong[maxm];
int mark[maxm];//每一块的长度
int a[maxm*2];//用于重构的时候临时存放所有数据
int block,num;
int n;//最开始数据个数
int all;//数据总个数
vector<int>temp[maxm];
void build(){
block=sqrt(n);//这个地方用all来分好像也可以啊,不太懂
num=all/block;//新的块数
if(all%block)num++;
for(int i=1;i<=n;i++){
belong[i]=(i-1)/block+1;//这题belong用处不大,可简化,
temp[belong[i]].push_back(a[i]);
mark[belong[i]]++;
}
}
void reset(){
int cnt=1;
for(int i=1;i<=num;i++){
int len=temp[i].size();
for(int j=0;j<len;j++){
a[cnt++]=temp[i][j];
}
temp[i].clear();
mark[i]=0;
}
}
void update(int x,int val){
int now=1;
while(x>mark[now]){
x-=mark[now];
now++;
}
temp[now].insert(temp[now].begin()+x-1,val);
mark[now]++;
all++;
if(mark[now]>2*block){
reset();
build();
}
}
void ffind(int x){
int now=1;
while(x>mark[now]){
x-=mark[now];
now++;
}
printf("%d\n",temp[now][x-1]);
}
int main(){
scanf("%d",&n);
all=n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build();
for(int i=1;i<=n;i++){
int d,x,y,c;
scanf("%d%d%d%d",&d,&x,&y,&c);
if(d==0){
update(x,y);
}else{
ffind(y);
}
}
return 0;
}