LOJ #6282. 数列分块入门 6
单点插入,单点查询。
因为要么插入O(1)查询O(sqrt(n)),要么插入O(sqrt(n)),查询O(1),所以选择代码难度更小的非链表暴力后移。
但是直接这样做虽然在随机数据上表现不错,但是在那些特意构造出的在某个区间疯狂加点的数据上会退化到与暴力差不多。
解决办法是每sqrt(n)次insert后将块重构,增大一点常数但使其保持均衡,从而保证复杂度不会被卡。
1 #include<iostream> 2 #include<cmath> 3 #include<vector> 4 #include<cstdio> 5 using namespace std; 6 7 void read(int& a){ 8 a = 0;char c = getchar(),l = c; 9 while(c < '0'||c > '9')l = c,c = getchar(); 10 while('0' <= c&&c <= '9')a = a*10+c-'0',c = getchar(); 11 if(l == '-')a *= -1; 12 } 13 14 vector<int> ve[350]; 15 int n,blo,cnti; 16 17 void insert(int x,int pl){ 18 int suf,p; 19 for(suf = 0,p = 1;suf+ve[p].size() < pl;p++) 20 suf += ve[p].size(); 21 pl -= suf+1;int y = *--ve[p].end(); 22 ve[p].push_back(y); 23 for(register int i = ve[p].size()-2;i > pl;i--)ve[p][i] = ve[p][i-1]; 24 ve[p][pl] = x; 25 } 26 27 int ask(int r){ 28 int suf,p; 29 for(suf = 0,p = 1;suf+ve[p].size() < r;p++) 30 suf += ve[p].size(); 31 return ve[p][r-suf-1]; 32 } 33 34 vector<int> all; 35 void reset(){ 36 all.clear(); 37 for(int i = 1;i <= (n-1)/blo+1;i++){ 38 for(int j = 0;j < ve[i].size();j++) 39 all.push_back(ve[i][j]); 40 ve[i].clear(); 41 } 42 n = all.size(),blo = sqrt(n); 43 for(int i = 0;i < n;i++) 44 ve[i/blo+1].push_back(all[i]); 45 } 46 47 int main(){ 48 read(n); blo = sqrt(n); 49 for(register int i = 1;i <= n;i++){ 50 int x; read(x); 51 ve[(i-1)/blo+1].push_back(x); 52 } 53 for(register int i = n;i > 0;i--){ 54 int opt,l,r,c; 55 read(opt),read(l),read(r),read(c); 56 switch(opt){ 57 case 0: 58 insert(r,l); cnti++; 59 if(cnti%blo == 0)reset(); 60 break; 61 case 1: 62 printf("%d\n",ask(r)); 63 break; 64 } 65 } 66 return 0; 67 }