#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}1≤n≤100000,?231≤others、mathrm{ans} leq 2^{31}-1ans≤231?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的主要内容,如果未能解决你的问题,请参考以下文章