块状链表(分块)2016.10.3

来源:互联网 发布:测风速的软件 编辑:程序博客网 时间:2024/06/06 01:08

参考:http://dongxicheng.org/structure/blocklink/

苏煜《对块状链表的一点研究》


一、概述

我们常用的两种线性数据结构是数组和链表,它们各有优缺点

数组特点是元素在内存中紧挨着存储,因而优点是定位快 O(1),缺点是插入删除慢 O(n)

而链表则不同,它通过指针将不同位置的元素链接起来,因而优缺点与数组正好相反:定位慢 O(n),插入删除快 O(1)


块状链表:将数组和链表的优点结合来,各种操作的时间复杂度均为 O(sqrt(n))


二、块状链表

从整体上看,块状链表是一个链表, 而在链表的每个节点上,以数组的形式存储一组元素

所谓的“块状”链表如图所示



三、基本操作

1、定位

先定位元素所在的链表节点,然后再定位该元素在数组中的位置

从链表头开始往后扫,每个节点记录本节点合法数据的长度,最终会定位为某一个块及块内偏移


2、分裂

将某个节点分裂成两个节点



3、插入

首先定位要插入的位置,然后将所在节点分裂成两个节点,并将数据放到第一个节点的末尾

如果要插入的是一大块数据,首先要将数据切成多个block(每个block对应一个块状链表的一个节点)并将这些block链起来,然后将它们插入那两个节点之间



4、删除

首先定位删除元素的位置,然后按照数组删除元素的方法删除该数据

如果删除一大块数据,首先要定位数据块首元素和末元素所在的位置,然后分别将它们所在的节点分裂成两个节点,最后删除首元素和末元素之间的节点即可



5、及时合并小分块




四、关键点和复杂度分析

该算法的核心是确定链表长度和每个节点的数组长度,以及怎么保证这个数组长度值?

设块状链表中元素总个数为 x,链表长度为 n,每个节点中数据长度为 m,则当 m=n=sqrt(x) 时,可保证 m 和 n 同时最小,此时各种操作的时间复杂度最低

在实际应用时,需维持块状链表的每个节点大小在 [sqrt(n)/2, 2*sqrt(n)],否则,块状链表会退化

维护方法是,适当的时候,对节点进行合并与分裂(维护本身不会使复杂度增加)



五、例题

1、BZOJ 1507 [NOI2003] Editor

参考:http://blog.csdn.net/greatwjj/article/details/20468249


#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 1500;int pos, size[maxn], List[maxn], Next[maxn];char s[2000000], c, data[maxn][maxn], cmd[20];void Init(void);int New_Node(void);void Del_Node(int t);void Find(int& p, int& b);void Split(int b, int p);void Fill_Block(int b, int n, char* str, int e);void Maintain(int b);void insert(int p, int n, char* str);void erase(int p, int n);void copy(int p, int n, char* str);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    int t, cur = 0, n;    Init();    scanf("%d", &t);    while (t--) {        scanf("%s", cmd);        if (cmd[0] == 'M') {            scanf("%d", &cur);        } else if (cmd[0] == 'I') {            scanf("%d", &n);            for (int i = 0; i < n;) {                c = getchar();                if (32 <= c && c <= 126) {                    s[i++] = c;                }            }            s[n] = '\0';            insert(cur, n, s);        } else if (cmd[0] == 'D') {            scanf("%d", &n);            erase(cur, n);        } else if (cmd[0] == 'G') {            scanf("%d", &n);            copy(cur, n, s);            s[n] = '\0';            printf("%s\n", s);        } else if (cmd[0] == 'P') {            --cur;        } else {            ++cur;        }    }    return 0;}int New_Node(void){    return List[pos++];}void Del_Node(int t){    List[--pos] = t;}void Find(int& p, int& b){    for (b = 0; b != -1 && p > size[b]; b = Next[b]) {        p -= size[b];    }}void Fill_Block(int b, int n, char* str, int e){    if (b == -1) {        return;    }    Next[b] = e;    size[b] = n;    memcpy(data[b], str, n);}void Split(int b, int p){    if (b == -1 || p == size[b]) {        return;    }    int t = New_Node();    Fill_Block(t, size[b]-p, data[b]+p, Next[b]);    Next[b] = t;    size[b] = p;}void Maintain(int b){    for (; b != -1; b = Next[b]) {        for (int t = Next[b]; t != -1 && size[b]+size[t] <= maxn; t = Next[b]) {            memcpy(data[b]+ size[b], data[t], size[t]);            size[b] += size[t];            Next[b] = Next[t];            Del_Node(t);        }    }}void insert(int p, int n, char* str){    int b, t, i;    Find(p, b);    Split(b, p);    for (i = 0; i+maxn <= n; i += maxn) {        t = New_Node();        Fill_Block(t, maxn, str+i, Next[b]);        Next[b] = t;        b = t;    }    if (n-i) {        t = New_Node();        Fill_Block(t, n-i, str+i, Next[b]);        Next[b] = t;    }    Maintain(b);}void erase(int p, int n){    int b, e;    Find(p, b);    Split(b, p);    for (e = Next[b]; e != -1 && n > size[e]; e = Next[e]) {        n -= size[e];    }    Split(e, n);    e = Next[e];    for (int t = Next[b]; t != e; t = Next[b]) {        Next[b] = Next[t];        Del_Node(t);    }    Maintain(b);}void copy(int p, int n, char* str){    int b, t, i;    Find(p, b);    i = min(n, size[b]-p);    memcpy(str, data[b]+p, i);    for (t = Next[b]; t != -1 && i+size[t] <= n; i += size[t], t = Next[t]) {        memcpy(str+i, data[t], size[t]);    }    if (n-i && t != -1) {        memcpy(str+i, data[t], n-i);    }}void Init(void){    for (int i = 1; i < maxn; ++i) {        List[i] = i;    }    pos = 1;    Next[0] = -1;    size[0] = 0;}


参考:http://blog.csdn.net/qq_18455665/article/details/50650215


#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 2e6 + 10;const int maxblock = 9000;char s[maxn], c, cmd[20];struct block {    char key[maxblock + 10];    int len;    block* next;    block(){key[0] = len = 0, next = NULL;}    void insert(int pos, int n);    void erase(int pos, int n);    void print(int pos, int n);    block* find(int pos, int &k);    void split(block *u, block *&r, int k);    void merge(block *u, block *r);};block* head;block List;int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    int t, cur = 0, n;    head = new block();    scanf("%d", &t);    while (t--) {        scanf("%s", cmd);        if (cmd[0] == 'M') {            scanf("%d", &cur);        } else if (cmd[0] == 'I') {            scanf("%d", &n);            for (int i = 0; i < n;) {                c = getchar();                if (32 <= c && c <= 126) {                    s[i++] = c;                }            }            s[n] = '\0';            List.insert(cur, n);        } else if (cmd[0] == 'D') {            scanf("%d", &n);            List.erase(cur, n);        } else if (cmd[0] == 'G') {            scanf("%d", &n);            List.print(cur, n);        } else if (cmd[0] == 'P') {            --cur;        } else {            ++cur;        }    }    return 0;}block* block::find(int pos, int &k){    int Count = 0;    block* t = head;    while (t->next) {        if (Count+t->len >= pos) {            break;        }        Count += t->len;        t = t->next;    }    k = pos - Count;    return t;}void block::split(block *u, block *&r, int k){    r = new block();    r->next = u->next, u->next = r;    strcpy(r->key, u->key+k);    r->len = u->len - k, u->len = k;    u->key[k] = '\0';}void block::merge(block *u, block *r){    strcat(u->key, r->key), u->len += r->len;    u->next = r->next, free(r), r = NULL;}void block::insert(int pos, int n){    int k;    block *u = find(pos, k), *r;    if (k == u->len) {        r = u->next;    } else {        split(u, r, k);    }    block *t = u;    int Count = 0;    while (n-Count >= maxblock) {        block *now = new block();        strncpy(now->key, s+Count, maxblock);        now->key[maxblock] = '\0', now->len = maxblock;        now->next = t->next, t->next = now;        t = now, Count += maxblock;    }    if (n-Count) {        block *now = new block();        strcpy(now->key, s+Count);        now->len = n - Count, now->next = t->next;        t->next = now, t = now;    }    if (u != head && u->next != NULL && u->len <= (maxblock/2) && u->next->len <= (maxblock/2)) {        if (t == u->next) {            merge(u, u->next), t = u;        } else {            merge(u, u->next);        }    }    if (t != head && t->len <= (maxblock/2) && r != NULL && r->len <= (maxblock/2)) {        merge(t, r);    }}void block::erase(int pos, int n){    int sk, ek;    block *st = find(pos, sk), *sr;    block *ed = find(pos+n, ek), *er;    split(ed, er, ek), split(st, sr, sk);    block *t = st->next;    while (t != er) {        block *t_free = t;        t = t->next;        free(t_free);        t_free = NULL;    }    st->next = er;    if (st->len <= (maxblock/2) && er->len <= (maxblock/2)) {        merge(st, er);    }}void block::print(int pos, int n){    int k, Count = 0;    block *now = find(pos+1, k);    --k;    while (now && Count < n) {        while (k < now->len && Count < n) {            putchar(now->key[k]);            ++Count, ++k;        }        now = now->next, k = 0;    }    putchar('\n');}


2、BZOJ 2002 [Hnoi2010] Bounce 弹飞绵羊

参考:http://hzwer.com/3505.html

http://www.bilibili.com/video/av6445624/


#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 200000 + 10;const int maxblock = 1000;int k[maxn], belong[maxn], steps[maxn], pt[maxn], Left[maxblock], Right[maxblock];int num, block, n, m, x, y, z;inline int read(void);void build(void);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    n = read();    for (int i = 1; i <= n; ++i) {        k[i] = read();    }    build();    for (int i = n; i > 0; --i) {        if (i+k[i] > n) {            steps[i] = 1;        } else if (belong[i] == belong[i+k[i]]) {            steps[i] = steps[i+k[i]] + 1, pt[i] = pt[i+k[i]];        } else {            steps[i] = 1, pt[i] = i + k[i];        }    }    m = read();    while (m--) {        x = read(), y = read();        ++y;        if (x == 1) {            int ans = 0;            while (1) {                ans += steps[y];                if (pt[y] == 0) {                    break;                }                y = pt[y];            }            printf("%d\n", ans);        } else {            z = read();            k[y] = z;            for (int i = y; i >= Left[belong[y]]; --i) {                if (belong[i] == belong[i+k[i]]) {                    steps[i] = steps[i+k[i]] + 1, pt[i] = pt[i+k[i]];                } else {                    steps[i] = 1, pt[i] = i + k[i];                }            }        }    }    return 0;}void build(void){    block = sqrt(n);    num = n / block;    if (n%block) {        ++num;    }    for (int i = 1; i <= num; ++i) {        Left[i] = (i-1) * block + 1;        Right[i] = i * block;    }    Right[num] = n;    for (int i = 1; i <= n; ++i) {        belong[i] = (i-1) / block + 1;    }}inline int read(void){    int ret = 0, flag = 1;    char c = getchar();    while (c > '9' || c < '0') {        if (c == '-') {            flag = -1;        }        c = getchar();    }    while ('0' <= c && c <= '9') {        ret = ret * 10 + c - '0';        c = getchar();    }    return ret * flag;}


3、CDOJ 1324 卿学姐与公主

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 1e5 + 10;const int maxblock = 400;ll hurt[maxn], belong[maxn], Left[maxblock], Right[maxblock], Max[maxblock];ll N, Q, t, p, x, L, R, num, block;ll read(void);void build(void);void update(void);ll query(void);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    N = read(), Q = read();    while (Q--) {        t = read();        if (t == 1) {            p = read(), x = read();            update();        } else {            L = read(), R = read();            printf("%lld\n", query());        }    }    return 0;}ll query(void){    ll ans = 0;    if (belong[L] == belong[R]) {        for (int i = L; i <= R; ++i) {            ans = max(ans, hurt[i]);        }    } else {        for (ll i = L; i <= Right[belong[L]]; ++i) {            ans = max(ans, hurt[x]);        }        for (ll i = belong[L] + 1; i < belong[R]; ++i) {            ans = max(ans, Max[i]);        }        for (ll i = Left[belong[R]]; i <= R; ++i) {            ans = max(ans, hurt[i]);        }    }    return ans;}void update(void){    hurt[p] += x;    Max[belong[p]] = max(Max[belong[p]], hurt[p]);}void build(void){    block = sqrt(N);    num = N / block;    if (N%block) {        ++num;    }    for (ll i = 1; i <= num; ++i) {        Left[i] = (i-1) * block + 1, Right[i] = i * block;    }    Right[num] = N;    for (ll i = 1; i <= N; ++i) {        belong[i] = (i-1) / block + 1;    }}ll read(void){    ll ret = 0;    char c = getchar();    while (c < '0' || c > '9') {        c = getchar();    }    while ('0' <= c && c <= '9') {        ret = ret * 10 + c - '0';        c = getchar();    }    return ret;}

4、BZOJ 4028 [HEOI2015] 公约数数列

参考:http://www.cnblogs.com/qscqesze/p/5203524.html


#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ll maxn = 1e5 + 10;const ll maxblock = 400;ll belong[maxn], Left[maxblock], Right[maxblock], a[maxn], gcd[maxn], Xor[maxn];ll block, num, n, id, q, x;char cmd[10];set<ll> Set[maxblock];void build(ll t);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    scanf("%lld", &n);    for (ll i = 1; i <= n; ++i) {        scanf("%lld", &a[i]);    }    block = sqrt(n);    num = n / block;    if (n%block) {        ++num;    }    for (ll i = 1; i <= num; ++i) {        Left[i] = (i-1) * block + 1, Right[i] = i * block;    }    Right[num] = n;    for (ll i = 1; i <= n; ++i) {        belong[i] = (i-1) / block + 1;    }    for (ll i = 1; i <= num; ++i) {        build(i);    }    scanf("%lld", &q);    while (q--) {        scanf("%s", cmd);        if (cmd[0] == 'M') {            scanf("%lld %lld", &id, &x), ++id;            a[id] = x;            build(belong[id]);        } else {            scanf("%lld", &x);            ll flag = 0, lgcd = 0, lxor = 0;            for (ll i = 1; i <= num; ++i) {                ll gcd_t = __gcd(lgcd, gcd[Right[i]]);                if (gcd_t != lgcd) {                    for (ll j = Left[i]; j <= Right[i]; ++j) {                        if ((lxor ^ Xor[j]) * __gcd(lgcd, gcd[j]) == x) {                            flag = j;                            break;                        }                    }                    if (flag) {                        break;                    }                } else {                    if (x%gcd_t == 0 && Set[i].count((x/gcd_t) ^ lxor)) {                        for (ll j = Left[i]; j <= Right[i]; ++j) {                            if ((lxor ^ Xor[j]) * lgcd == x) {                                flag = j;                                break;                            }                        }                        if (flag) {                            break;                        }                    }                }                lgcd = gcd_t, lxor ^= Xor[Right[i]];            }            if (flag) {                printf("%lld\n", flag-1);            } else {                printf("no\n");            }        }    }    return 0;}void build(ll t){    Set[t].clear();    gcd[Left[t]] = Xor[Left[t]] = a[Left[t]];    Set[t].insert(Xor[Left[t]]);    for (ll i = Left[t] + 1; i <= Right[t]; ++i) {        gcd[i] = __gcd(gcd[i-1], a[i]), Xor[i] = Xor[i-1] ^ a[i];        Set[t].insert(Xor[i]);    }}


1 0
原创粉丝点击