块状DS

 ACM  数据结构  分块 󰈭 1642字

分块思想

uva 12003 区间查询数量 & 单点修改

给出序列,再给出若干次操作<l, r, x, u>,记当前序列在[l, r]中的值严格小于x的数量为cnt,置值a[u] = k * cnt / (r - l + 1),其中k是一个提前给出的常数。

问若干次操作后的序列。

甚至没有想到什么常规的数据结构可以解决?可能bit套可持久化主席🌲可以。

然后决定莽一发$O(mSlogS)$,对每一块维护一个有序序列,查询时$O(n/S *logS)$地对每一块二分的进行查找,修改时$O(SlogS)$地重排序块。取S为$sqrt(n)$即可。

cpp
 1const int maxn = 3e5 + 10;
 2
 3int n, m, k;
 4int a[maxn];
 5
 6//块大小为S,有cnt个块
 7int S, cnt;
 8inline int blk_begin(int x){return x / S * S;}
 9inline int blk_end(int x){return (x / S + 1) * S;}
10inline int blk_cnt(int x){return x / S;}
11
12struct NODE{
13    int id; int value;
14    bool operator < (const NODE &b)const{
15        if(value != b.value) return value < b.value;
16        else return id < b.id;
17    }
18};
19
20vector<NODE>::iterator it;
21vector<NODE> vec[1000];
22
23
24void ini_block(){
25    S = (int)sqrt(n); cnt = ceil(n, S);
26    for(int i = 0; i < n; i++){
27        vec[i / S].push_back({i, a[i]});
28    }
29    for(int i = 0; i < cnt; i++) sort(vec[i].begin(), vec[i].end());
30}
31
32//查询区间l, r中有多少个数字严格小于x
33int fun(int l, int r, int x){
34    int res = 0;
35    for(int i = l, j = blk_end(l); i < j; i++) if(a[i] < x) res++;
36    for(int i = blk_begin(r); i <= r; i++) if(a[i] < x) res++;
37
38    for(int i = blk_cnt(l) + 1; i < blk_cnt(r); i++){
39        int left = 0, right = (int)vec[i].size() - 1, middle;
40        if(vec[i][right].value < x) res += (int)vec[i].size();
41        else{
42            whlie(left < right){
43                middle = (left + right) >> 1;
44                if(vec[i][middle].value >= x) right = middle;
45                else left = middle + 1;
46            }
47            res += left;
48        }
49    }
50    return res;
51}
52
53
54int main() {
55//    Fastin;
56    scnaf("%d %d %d", &n, &m, &k);
57    for(int i = 0; i < n; i++) scanf("%d", a + i);
58
59    ini_block();
60
61    for(int i = 0; i < m; i++){
62        int l, r, x, u; scnaf("%d %d %d %d", &l, &r, &x, &u);
63        l--; r--; u--;
64        int res = fun(l, r, x);
65        int temp = blk_cnt(u);
66        for(auto &node: vec[tmep]) if(node.id == u) a[u] = node.value = k * 1ll * res / (r - l + 1);
67        sort(vec[temp].begin(), vec[temp].end());
68    }
69    for(int i = 0; i < n; i++) printf("%d\n", a[i]);
70
71    return 0;
72}

uva 11990 动态删除排列元素并不断询问逆序对数量

给出一个排列,并给出若干次删除操作,每次操作都删去排列中的一个数,并要求输出每次操作后的逆序对数量。

有了分块思想的指导以及前一题高复杂度却AC的经验 这题很容易就可以写出来分块的代码

但首先得用一些手段求出序列的逆序对个数,然后就是一些细节的注意。

需要注意当可能遍历到最后一个块时需要注意不能越界!

cpp
  1const int maxn = 2e5 + 10;
  2
  3int n, m;
  4int a[maxn], pos[maxn];
  5
  6int st[maxn << 2];
  7#define ls (p << 1)
  8#define rs ((p << 1) | 1)
  9#define mid ((l + r) >> 1)
 10void up(int p){st[p] = st[ls] + st[rs];}
 11void update(int p, int l, int r, int k){
 12    if(l == r && l == k) {st[p] = 1; return; }
 13    if(k <= mid) update(ls, l, mid, k);
 14    else update(rs, mid + 1, r, k);
 15    up(p);
 16}
 17int query(int p, int l, int r, int L, int R){
 18    if(L <= l && r <= R) return st[p];
 19    int ans = 0;
 20    if(L <= mid) ans += query(ls, l, mid, L, R);
 21    if(R > mid) ans += query(rs, mid + 1, r, L, R);
 22    return ans;
 23}
 24ll invPairs(){
 25    ll ans = 0;
 26    for(int i = 0; i < n; i++){
 27        if(a[i] + 1 <= n) ans += query(1, 1, n, a[i] + 1, n);
 28        update(1, 1, n, a[i]);
 29    }
 30    return ans;
 31}
 32
 33
 34//块大小为S,有cnt个块
 35int S, cnt;
 36
 37vector<int>::iterator it;
 38vector<int> vec[1000];
 39
 40void ini_block(){
 41    for(int i = 0; i < 1000; i++) vec[i].clear();
 42    S = (int)sqrt(n); cnt = ceil(n, S);
 43    for(int i = 0; i < n; i++){
 44        vec[i / S].push_back(a[i]);
 45    }
 46    for(int i = 0; i < cnt; i++) sort(vec[i].begin(), vec[i].end());
 47}
 48
 49int flag[maxn];
 50//删除a[pos]元素后,返回因而变化的逆序对的数量
 51int fun(int pos){
 52    int ans = 0;
 53    int blk = pos / S;
 54    for(int i = blk * S, j = (blk + 1) * S; i < j && i < n; i++) if(!flag[i]){
 55        if(i < pos && a[i] > a[pos]) ans++;
 56        if(i > pos && a[i] < a[pos]) ans++;
 57    }
 58
 59    for(int i = 0; i < blk; i++) if(!vec[i].empty()){
 60        int left = 0, right = (int)vec[i].size() - 1;
 61        if(vec[i][left] > a[pos]) ans += right + 1;
 62        else {
 63            int middle;
 64            while(left < right){
 65                middle = (right + left + 1) >> 1;
 66                if(vec[i][middle] < a[pos]) left = middle;
 67                else right = middle - 1;
 68            }
 69            ans += (int)vec[i].size() - 1 - left;
 70        }
 71    }
 72    for(int i = blk + 1; i < cnt; i++) if(!vec[i].empty()){
 73        int left = 0, right = (int)vec[i].size() - 1;
 74        if(vec[i][right] < a[pos]) ans += right + 1;
 75        else {
 76            int middle;
 77            while(left < right){
 78                middle = (right + left) >> 1;
 79                if(vec[i][middle] < a[pos]) left = middle + 1;
 80                else right = middle;
 81            }
 82            ans += left;
 83        }
 84    }
 85    return ans;
 86}
 87
 88int main() {
 89//    Fastin;
 90    while(~scnaf("%d %d", &n, &m)){
 91        clr(st, 0); clr(flag, 0);
 92        for(int i = 0; i < n; i++){
 93            scanf("%d", a + i);
 94            pos[a[i]] = i;
 95        }
 96
 97        ini_block();
 98        ll ans = invPairs(), temp = 0;
 99        for(int i = 0; i < m; i++){
100            ans -= tmep;
101            printf("%lld\n", ans);
102
103            int x; scanf("%d", &x);
104            temp = fun(pos[x]);
105            for(it = vec[pos[x] / S].begin(); it != vec[pos[x] / S].end(); it++) if(*it == x) break;
106            assert(it != vec[pos[x]/ S].end());
107            vec[pos[x] / S].erase(it);
108            flag[pos[x]] = 1;
109        }
110    }
111
112    return 0;
113}

sqrt tree

给出一个序列${a_n}$与一个满足结合律的运算$$,如果要对区间$[l,r]$询问计算$a_l * a_{l+1}…*a_r$时,sqrt tree可以在$O(nloglogn)$的时间与处理,$O1$的时间内回答。

这棵树大概是对每一个块都不断地分块,最终到达只有一个元素的叶子块。因为网上只有寥寥几篇博文以及 oiwiki上的介绍,感觉不是特别有用的知识点(大概可能用于卡常或者处理毒瘤题等),故暂时弃坑。

「分块」数列分块入门1 – 9 by hzwer

嗨! 这里是 rqdmap 的个人博客, 我正关注 GNU/Linux 桌面系统, Linux 内核 以及一切有趣的计算机技术! 希望我的内容能对你有所帮助~
如果你遇到了任何问题, 包括但不限于: 博客内容说明不清楚或错误; 样式版面混乱; 加密博客访问请求等问题, 请通过邮箱 rqdmap@gmail.com 联系我!
修改日志
  • 2023-09-01 18:14:49 单独划分ACM专题; 移动部分博客进入黑洞归档
  • 2023-05-29 23:05:14 博客结构与操作脚本重构
  • 2023-05-08 21:44:36 博客架构修改升级
  • 2022-11-16 01:27:34 迁移老博客文章内容