1. 程式人生 > >#6282. 數列分塊入門 6

#6282. 數列分塊入門 6

sin 維護 查找 接下來 以及 思路 style vector back

題目鏈接: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\n",find(l,r)); } }

 

#6282. 數列分塊入門 6