Chaos Slover 补档计划 - 可持久化数据结构

2015.11.13 14:19 Fri| 11 visits oi_2016| ChaosSlover补档计划| Text

可持久化数据结构

就是可持久化线段树啊、可持久化并查集啊、可持久化可并堆啊……

吃了我的万艾可,你也可以持久化……

POJ2104 K-th Number

大概是许多人可持久化线段树的第一题吧……经典的区间第 K 大,然而我的代码不加静态开点过不了……偷懒不写离散化也过不了……

刷一下当复习了。上一次刷这道题竟然是去年的今天!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <bits/stdc++.h>
using namespace std;

#define N 100005
#define S 10000005

struct node
{
    node *son[2]; int size;
    void *operator new(size_t s);
} newnode[S];

void *node::operator new(size_t s)
{
    static node *p = newnode;
    return p++;
}

void insert(node *pre, node *&pos, int l, int r, int x)
{
    pos = new node();
    *pos = *pre; ++pos->size;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (x <= mid)
        insert(pre->son[0], pos->son[0], l, mid, x);
    else insert(pre->son[1], pos->son[1], mid + 1, r, x);
}

int query(node *pre, node *pos, int l, int r, int x)
{
    if (l == r) return l;
    int mid = (l + r) >> 1;
    int t = pos->son[0]->size - pre->son[0]->size;
    if (t >= x)
        return query(pre->son[0], pos->son[0], l, mid, x);
    return query(pre->son[1], pos->son[1], mid + 1, r, x - t);
}

int n, m, a[N];
node *root[N];
vector<int> v;

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i], v.push_back(a[i]);
    sort(v.begin(), v.end());
    root[0] = new node();
    root[0]->son[0] = root[0]->son[1] = root[0];
    for (int i = 1; i <= n; ++i)
        a[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin(),
        insert(root[i - 1], root[i], 0, n, a[i]);
    for (int x, y, z, i = 1; i <= m; ++i)
        cin >> x >> y >> z,
        cout << v[query(root[x - 1], root[y], 0, n, z)] << '\n';
    return 0;
}

BZOJ3524 [Poi2014]Couriers

同样是主席树的区间查询,查询的时候如果某一个儿子的 size 大于 (r-l+1)/2 就去找这个儿子,否则直接 return 0。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>
using namespace std;

#define N 500005
#define S 10000005

struct node
{
    node *son[2]; int size;
    void *operator new(size_t s);
} newnode[S];

void *node::operator new(size_t s)
{
    static node *p = newnode;
    return p++;
}

void insert(node *pre, node *&pos, int l, int r, int x)
{
    pos = new node();
    *pos = *pre; ++pos->size;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (x <= mid)
        insert(pre->son[0], pos->son[0], l, mid, x);
    else insert(pre->son[1], pos->son[1], mid + 1, r, x);
}

int query(node *pre, node *pos, int l, int r, int x)
{
    if (l == r) return l;
    int mid = (l + r) >> 1;
    if (pos->son[0]->size - pre->son[0]->size > x)
        return query(pre->son[0], pos->son[0], l, mid, x);
    if (pos->son[1]->size - pre->son[1]->size > x)
        return query(pre->son[1], pos->son[1], mid + 1, r, x);
    return 0;
}

int n, m, a[N];
node *root[N];

int main()
{
    cin >> n >> m;
    root[0] = new node();
    root[0]->son[0] = root[0]->son[1] = root[0];
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), insert(root[i - 1], root[i], 0, n, a[i]);
    for (int x, y, i = 1; i <= m; ++i)
        scanf("%d%d", &x, &y),
        printf("%d\n", query(root[x - 1], root[y], 0, n, (y - x + 1) / 2));
    return 0;
}

BZOJ1901 Zju2112 Dynamic Rankings

这道题多了一个修改操作。于是我们可以用树状数组维护一列线段树,从而进行加减操作。然而这个题并不是可持久化线段树(当然没有办法可持久化),而只是函数式线段树,即主席树,其中每一棵小线段树其实只是一个动态加点的线段树而已。于是这道题在时间和空间上都比之前的不修改查询区间第 k 小值多了一个 log。

然而这种题无论怎样都感觉加一个离散化很闲得慌。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
#include <bits/stdc++.h>
using namespace std;

#define N 10005
#define S 10000005

struct node
{
    int size;
    node *son[2];
    void *operator new(size_t s);
} newnode[S];

void *node::operator new(size_t s)
{
    static node *p = newnode;
    return p++;
}

int n, m, a[N], x[N], y[N], z[N];
node *root[N];
vector<int> v;

void insert(node *pos, int l, int r, int x, int y)
{
    if (!pos->son[0]) pos->son[0] = new node();
    if (!pos->son[1]) pos->son[1] = new node();
    pos->size += y;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (x <= mid) insert(pos->son[0], l, mid, x, y);
    else insert(pos->son[1], mid + 1, r, x, y);
}

int query(vector<node*> pre, vector<node*> pos, int l, int r, int x)
{
    vector<node*>::iterator i;
    if (l == r) return l;
    int t = 0, mid = (l + r) >> 1;
    for (i = pos.begin(); i != pos.end(); ++i)
        if ((*i)->son[0]) t += (*i)->son[0]->size;
    for (i = pre.begin(); i != pre.end(); ++i)
        if ((*i)->son[0]) t -= (*i)->son[0]->size;
    if (t >= x)
    {
        for (i = pos.begin(); i != pos.end(); ++i)
            if ((*i)->son[0]) (*i) = (*i)->son[0];
        for (i = pre.begin(); i != pre.end(); ++i)
            if ((*i)->son[0]) (*i) = (*i)->son[0];
        return query(pre, pos, l, mid, x);
    }
    else
    {
        for (i = pos.begin(); i != pos.end(); ++i)
            if ((*i)->son[1]) (*i) = (*i)->son[1];
        for (i = pre.begin(); i != pre.end(); ++i)
            if ((*i)->son[1]) (*i) = (*i)->son[1];
        return query(pre, pos, mid + 1, r, x - t);
    }
}

void modify(int x, int y, int z)
{
    for (int i = x; i <= n; i += i & -i)
        insert(root[i], 0, n + m, y, z);
}

vector<node*> get(int x)
{
    vector<node*> re;
    for (int i = x; i; i -= i & -i)
        re.push_back(root[i]);
    return re;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        root[i] = new node();
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), v.push_back(a[i]);
    char opt[5];
    for (int i = 1; i <= m; ++i)
    {
        scanf("%s", &opt);
        if (opt[0] == 'C')
            scanf("%d%d", &x[i], &y[i]), v.push_back(y[i]);
        else
            scanf("%d%d%d", &x[i], &y[i], &z[i]);
    }
    sort(v.begin(), v.end());
    for (int i = 1; i <= n; ++i)
        a[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin();
    for (int i = 1; i <= m; ++i)
        if (!z[i])
            y[i] = lower_bound(v.begin(), v.end(), y[i]) - v.begin();
    for (int i = 1; i <= n; ++i)
        modify(i, a[i], 1);
    for (int i = 1; i <= m; ++i)
        if (!z[i])
            modify(x[i], a[x[i]], -1), modify(x[i], a[x[i]] = y[i], 1);
        else
            printf("%d\n", v[query(get(x[i] - 1), get(y[i]), 0, n + m, z[i])]);
    return 0;
}

BZOJ1146 网络管理Network

当初写了 8KB……挖坑过两天重写一遍。

BZOJ3261 最大异或和

唔……刚刚敲了一遍 lct 模板(天哪,我竟然会写 lct 了),用时十七分钟,一遍通过,一字节不差。大概有进步?以后争取写数据结构都不需要调试(毕竟指针什么的调试起来实在是太麻烦了)。

唔……刚刚又敲了一遍 Dinic 模板,用时不到十分钟,犯了少许小错误(忘记写 if(x == t) return f;,以及把一个 edge[i].next 写成了 edge[i].to)。以后争取写算法也都不需要调试。

唔……刚刚敲了一遍 AC 自动机模板——我已经多久没有写 Trie 树了。再默写一遍,用时七分钟。那么现在开始玩可持久化 Trie 吧!

这道题要求的是最大的后缀异或和,可以转化为求前缀异或和来解决,在可持久化的 Trie 树上贪心即可。为了实现简洁,可以在 Trie 中最前面加入一个 0。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <bits/stdc++.h>
using namespace std;

#define N 600005

struct node
{
    node *son[2];
    int size;
};

node *root[N];

void insert(node *pre, node *&pos, int k, int d)
{
    pos = new node();
    *pos = *pre; ++pos->size;
    if (d == -1) return;
    if ((k >> d) & 1)
        insert(pre->son[1], pos->son[1], k, d - 1);
    else
        insert(pre->son[0], pos->son[0], k, d - 1);
}

int query(node *pre, node *pos, int k, int d)
{
    int re = 0;
    for (int t = 25; t >= 0; --t)
    {
        if ((k >> t) & 1)
        {
            if (pos->son[0]->size - pre->son[0]->size)
                pos = pos->son[0], pre = pre->son[0];
            else
                re ^= (1 << t), pos = pos->son[1], pre = pre->son[1];
        }
        else
        {
            if (pos->son[1]->size - pre->son[1]->size)
                re ^= (1 << t), pos = pos->son[1], pre = pre->son[1];
            else
                pos = pos->son[0], pre = pre->son[0];
        }
    }
    return re;
}

int n, m, a[N];

int main()
{
    cin >> n >> m;
    root[0] = new node();
    root[0]->son[0] = root[0]->son[1] = root[0];
    insert(root[0], root[1], 0, 25);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), a[i] ^= a[i - 1],
        insert(root[i], root[i + 1], a[i], 25);
    char opt[5];
    for (int l, r, x, i = 1; i <= m; ++i)
    {
        scanf("%s", opt);
        if (opt[0] == 'A')
            scanf("%d", &a[++n]), a[n] ^= a[n - 1],
            insert(root[n], root[n + 1], a[n], 25);
        else
        {
            scanf("%d%d%d", &l, &r, &x);
            x ^= a[n];
            printf("%d\n", x ^ query(root[l - 1], root[r], x, 25));
        }
    }
    return 0;
}

BZOJ4103 [Thu Summer Camp 2015]异或运算

由于数据范围十分之诡异,我们考虑对于数列 Y 建立可持久化 Trie 树。求第 K 大值有些棘手,我们可以转化为第 (d-u+1)*(r-l+1)-K 小值。对于一个询问,把 X 数列的从 u 到 d 位一起放进可持久化 Trie 里面跑就好了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <bits/stdc++.h>
using namespace std;

struct node
{
    node *son[2];
    int size;
    void *operator new(size_t s);
} newnode[10000005];

void *node::operator new(size_t s)
{
    static node *p = newnode;
    return p++;
}

int n, m, p, u, d, l, r, k, a[300005], b[300005];
node *root[300005], *pre[300005], *pos[300005];

void insert(node *pre, node *&pos, int x, int d)
{
    pos = new node();
    *pos = *pre; ++pos->size;
    if (d == -1) return;
    if ((x >> d) & 1)
        insert(pre->son[1], pos->son[1], x, d - 1);
    else
        insert(pre->son[0], pos->son[0], x, d - 1);
}

int query(int k)
{
    int re = 0;
    for (int i = u; i <= d; ++i)
        pre[i] = root[l - 1], pos[i] = root[r];
    for (int i = 30; i >= 0; --i)
    {
        int t = 0;
        for (int j = u; j <= d; ++j)
        {
            if ((a[j] >> i) & 1)
                t += pos[j]->son[1]->size - pre[j]->son[1]->size;
            else
                t += pos[j]->son[0]->size - pre[j]->son[0]->size;
        }
        if (t >= k)
        {
            for (int j = u; j <= d; ++j)
            {
                if ((a[j] >> i) & 1)
                    pre[j] = pre[j]->son[1], pos[j] = pos[j]->son[1];
                else
                    pre[j] = pre[j]->son[0], pos[j] = pos[j]->son[0];
            }
        }
        else
        {
            re ^= 1 << i; k -= t;
            for (int j = u; j <= d; ++j)
            {
                if ((a[j] >> i) & 1)
                    pre[j] = pre[j]->son[0], pos[j] = pos[j]->son[0];
                else
                    pre[j] = pre[j]->son[1], pos[j] = pos[j]->son[1];
            }
        }
    }
    return re;
}

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    root[0] = new node(); root[0]->size = 0;
    root[0]->son[0] = root[0]->son[1] = root[0];
    for (int i = 1; i <= m; ++i)
        b[i] = read(),
        insert(root[i - 1], root[i], b[i], 30);
    cin >> p;
    for (int i = 1; i <= p; ++i)
    {
        u = read(); d = read(); l = read(); r = read(); k = read();
        k = (d - u + 1) * (r - l + 1) - k + 1;
        printf("%d\n", query(k));
    }
}