算法笔记--可持久化线段树

参考:https://www.cnblogs.com/RabbitHu/p/segtree.html

模板:

const int N = 1e5 + 5, M = 2e6 + 5;//M为节点个数,为Q*log(N)
int root[N], lson[M], rson[M], value[M], tot = 0;
//建树
void build(int &x, int l, int r) {
    x = ++tot;
    if(l == r) {
        scanf("%d", &value[x]);
        return ;
    }
    int m = (l+r) >> 1;
    build(lson[x], l, m);
    build(rson[x], m+1, r);
    value[x] = value[lson[x]] + value[rson[x]];
}
// 将某个历史版本p位置的值加v
void update(int old, int &x, int p, int v, int l, int r) {
    x = ++tot;
    lson[x] = lson[old], rson[x] = rson[old], value[x] = value[old] + v;
    if(l == r) return ;
    int m = (l+r) >> 1;
    if(p <= m) update(lson[x], lson[x], p, v, l, m);
    else update(rson[x], rson[x], p, v, m+1, r);
}
//访问某个历史版本L到R的区间和
int query(int L, int R, int x, int l, int r) {
    if(L <= l && r <= R) return value[x];
    int m = (l+r) >> 1, ans = 0;
    if(L <= m) ans += query(L, R, lson[x], l, m);
    if(R > m) ans += query(L, R, rson[x], m+1, r);
    return ans;
}

 1175 区间中第K大的数

思路:

将求第k小的问题的问题转换成权值统计问题,采用可持久化线段树维护

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pii pair<int, int>
#define piii pair<pii, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head

const int N = 5e4 + 5, M = 2e6 + 5;//M为节点个数,为Q*log(N)
int root[N], lson[M], rson[M], value[M], tot = 0;
int a[N], v[N];
vector<int>vc;
void build(int &x, int l, int r) {
    x = ++tot;
    if(l == r) {
        value[x] = 0;
        return ;
    }
    int m = (l+r) >> 1;
    build(lson[x], l, m);
    build(rson[x], m+1, r);
    value[x] = value[lson[x]] + value[rson[x]];
}
void update(int old, int &x, int p, int v, int l, int r) {
    x = ++tot;
    lson[x] = lson[old], rson[x] = rson[old], value[x] = value[old] + v;
    if(l == r) return ;
    int m = (l+r) >> 1;
    if(p <= m) update(lson[x], lson[x], p, v, l, m);
    else update(rson[x], rson[x], p, v, m+1, r);
}
int query(int x, int y, int l, int r, int k) {
    if(l == r) return l;
    int m = (l+r) >> 1, cnt = value[lson[y]] - value[lson[x]];
    if(k <= cnt) return query(lson[x], lson[y], l, m, k);
    else return query(rson[x], rson[y], m+1, r, k-cnt);
}
int main() {
    int n, l, r, k, q;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), vc.pb(a[i]);
    sort(vc.begin(), vc.end());
    vc.erase(unique(vc.begin(), vc.end()), vc.end());
    for (int i = 1; i <= n; i++) {
       int id = lower_bound(vc.begin(), vc.end(), a[i]) - vc.begin() + 1;
       v[id] = a[i];
       a[i] = id;
    }
    build(root[0], 1, n);
    for (int i = 1; i <= n; i++) {
        update(root[i-1], root[i], a[i], 1, 1, n);
    }
    scanf("%d", &q);
    while(q--) {
        scanf("%d %d %d", &l, &r, &k); 
        //cout << query(root[l], root[r+1], 1, n, (r-l+1-k+1)) << endl;
        printf("%d\n", v[query(root[l], root[r+1], 1, n, (r-l+1-k+1))]);
    }
    return 0;
}
View Code

zoj 2112

思路:

树状数组套可持久线段树,注意一开始要建静态主席树,否则会爆栈

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pii pair<int, int>
#define piii pair<pii, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head

const int N = 5e4 + 5, M = 1e4 + 5;
int a[N], root[N], bitroot[N], value[N*40], lson[N*40], rson[N*40], use[N], tot, up, n;
piii Q[M];
vector<int>vc;
char s[M][10];
void build(int &x, int l, int r) {
    x = ++tot;
    if(l == r) {
        value[x] = 0;
        return ;
    }
    int m = l+r >> 1;
    build(lson[x], l, m);
    build(rson[x], m+1, r);
    value[x] = value[lson[x]] + value[rson[x]];
}
int update(int old, int &x, int p, int v, int l, int r) {
    x = ++tot;
    lson[x] = lson[old], rson[x] = rson[old], value[x] = value[old] + v;
    if(l == r) return 0;
    int m = l+r >> 1;
    if(p <= m) update(lson[old], lson[x], p, v, l, m);
    else update(rson[old], rson[x], p, v, m+1, r);
    return 0;
}
void add(int x, int pos, int v) {
    while(x <= n) {
        update(bitroot[x], bitroot[x], pos, v, 1, up);
        x += x&-x;
    }
}
int sum(int x) {
    int ans = 0;
    while(x) {
        ans += value[lson[use[x]]];
        x -= x&-x;
    }
    return ans;
}
int query(int l, int r, int k) {
    for (int i = l-1; i; i -= i&-i) use[i] = bitroot[i];
    for (int i = r; i; i -= i&-i) use[i] = bitroot[i];
    int lroot = root[l-1], rroot = `root[r];
    int ll = 1 , rr = up, m = ll+rr >> 1;
    while(ll < rr) {
        int cnt = sum(r) - sum(l-1) + value[lson[rroot]] - value[lson[lroot]];
        if(k <= cnt) {
            rr = m;
            for (int i = l-1; i; i -= i&-i) use[i] = lson[use[i]];
            for (int i = r; i; i -= i&-i) use[i] = lson[use[i]];
            lroot = lson[lroot];
            rroot = lson[rroot];
        }
        else {
            ll = m+1;
            k -= cnt;
            for (int i = l-1; i; i -= i&-i) use[i] = rson[use[i]];
            for (int i = r; i; i -= i&-i) use[i] = rson[use[i]];
            lroot = rson[lroot];
            rroot = rson[rroot];
        }
        m = ll+rr >> 1;
    }
    return ll;
}
int main() {
    int T, m;
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &n, &m);
        vc.clear();
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]), vc.pb(a[i]);
        for (int i = 0; i < m; i++) {
            scanf("%s", s[i]);
            if(s[i][0] == 'Q') {
                scanf("%d %d %d", &Q[i].fi.fi, &Q[i].fi.se, &Q[i].se);
            }
            else {
                scanf("%d %d", &Q[i].fi.fi, &Q[i].fi.se);
                vc.pb(Q[i].fi.se);
            }
        }
        sort(vc.begin(), vc.end());
        vc.erase(unique(vc.begin(), vc.end()), vc.end());

        up = (int)vc.size();
        tot = 0;
        build(root[0], 1, up);
        for (int i = 1; i <= n; i++) a[i] = lower_bound(vc.begin(), vc.end(), a[i]) - vc.begin() + 1;
        for (int i = 1; i <= n; i++) update(root[i-1], root[i], a[i], 1, 1, up);//要开静态主席树,否则会爆栈,SegmentFault一整天
        for (int i = 1; i <= n; i++) bitroot[i] = root[0];//树状数组每个点对应一个权值线段树,要单独开
        for (int i = 0; i < m; i++) {
            if(s[i][0] == 'Q') printf("%d\n", vc[query(Q[i].fi.fi, Q[i].fi.se, Q[i].se) - 1]);
            else {
                add(Q[i].fi.fi, a[Q[i].fi.fi], -1);
                int t = lower_bound(vc.begin(), vc.end(), Q[i].fi.se) - vc.begin() + 1;
                add(Q[i].fi.fi, t, 1);
                a[Q[i].fi.fi] = t;
            }
        }
    }
    return 0;
}
/*
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
*/
View Code

luogu P2617

思路:

和上面一样,但空间足够,所以原数组不用开静态主席树

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define fi first 
#define se second
#define LL long long
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define pii pair<int, int>
#define piii pair<pii, int>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int N = 1e5 + 5, M = 1e5 + 5;
int a[N], root[N], value[(N+M)*300], lson[(N+M)*300], rson[(N+M)*300], use[N], tot, up, n, m;
piii Q[M];
vector<int>vc;
char s[M][10];
void build(int &x, int l, int r) {
    x = ++tot;
    if(l == r) {
        value[x] = 0;
        return ;
    }
    int m = l+r >> 1;
    build(lson[x], l, m);
    build(rson[x], m+1, r);
    value[x] = value[lson[x]] + value[rson[x]];
}
int update(int old, int &x, int p, int v, int l, int r) {
    x = ++tot;
    lson[x] = lson[old], rson[x] = rson[old], value[x] = value[old] + v;
    if(l == r) return 0;
    int m = l+r >> 1;
    if(p <= m) update(lson[old], lson[x], p, v, l, m);
    else update(rson[old], rson[x], p, v, m+1, r);
    return 0;
}
void add(int x, int pos, int v) {
    while(x <= n) {
        update(root[x], root[x], pos, v, 1, up);
        x += x&-x;
    }
}
int sum(int x) {
    int ans = 0;
    while(x) {
        ans += value[lson[use[x]]];
        x -= x&-x;
    }
    return ans;
}
int query(int l, int r, int k) {
    for (int i = l-1; i; i -= i&-i) use[i] = root[i];
    for (int i = r; i; i -= i&-i) use[i] = root[i];
    int ll = 1 , rr = up, m = ll+rr >> 1;
    while(ll < rr) {
        int cnt = sum(r) - sum(l-1);
        if(k <= cnt) {
            rr = m;
            for (int i = l-1; i; i -= i&-i) use[i] = lson[use[i]];
            for (int i = r; i; i -= i&-i) use[i] = lson[use[i]];
        }
        else {
            ll = m+1;
            k -= cnt;
            for (int i = l-1; i; i -= i&-i) use[i] = rson[use[i]];
            for (int i = r; i; i -= i&-i) use[i] = rson[use[i]];
        }
        m = ll+rr >> 1;
    }
    return ll;
}
int main() {
    scanf("%d %d", &n, &m);
    vc.clear();
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), vc.pb(a[i]);
    for (int i = 0; i < m; i++) {
        scanf("%s", s[i]);
        if(s[i][0] == 'Q') {
            scanf("%d %d %d", &Q[i].fi.fi, &Q[i].fi.se, &Q[i].se);
        }
        else {
            scanf("%d %d", &Q[i].fi.fi, &Q[i].fi.se);
            vc.pb(Q[i].fi.se);
        }
    }
    sort(vc.begin(), vc.end());
    vc.erase(unique(vc.begin(), vc.end()), vc.end());
    up = (int)vc.size();
    tot = 0;
    build(root[0], 1, up);
    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(vc.begin(), vc.end(), a[i]) - vc.begin() + 1;
        add(i, a[i], 1);
    }
    for (int i = 0; i < m; i++) {
        if(s[i][0] == 'Q') printf("%d\n", vc[query(Q[i].fi.fi, Q[i].fi.se, Q[i].se) - 1]);
        else {
            add(Q[i].fi.fi, a[Q[i].fi.fi], -1);
            int t = lower_bound(vc.begin(), vc.end(), Q[i].fi.se) - vc.begin() + 1;
            add(Q[i].fi.fi, t, 1);
            a[Q[i].fi.fi] = t;
        }
    }
    return 0;
}
View Code

 蓝桥杯 第几小

思路:

树状数组套可持久线段树,但被卡常了

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define fi first 
#define se second
#define LL long long
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define pii pair<int, int>
#define piii pair<pii, int>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int N = 1e5 + 5, M = 2e5 + 5;
int a[N], root[N], bit[N], use[N], value[(N+M)*70], lson[(N+M)*70], rson[(N+M)*70], tot, n, m;
int op, l, r, p, x, y;
inline void build(int &x, int l, int r) {
    x = ++tot;
    if(l == r) {
        value[x] = 0;
        return ;
    }
    int m = l+r >> 1;
    build(lson[x], l, m);
    build(rson[x], m+1, r);
    value[x] = value[lson[x]] + value[rson[x]];
}
inline int update(int old, int &x, int p, int v, int l, int r) {
    x = ++tot;
    lson[x] = lson[old], rson[x] = rson[old], value[x] = value[old] + v;
    if(l == r) return 0;
    int m = l+r >> 1;
    if(p <= m) update(lson[old], lson[x], p, v, l, m);
    else update(rson[old], rson[x], p, v, m+1, r);
    return 0;
}
inline void add(int x, int pos, int v) {
    while(x <= n) {
        update(bit[x], bit[x], pos, v, 1, 1000000);
        x += x&-x;
    }
}
inline int sum(int x) {
    int ans = 0;
    while(x) {
        ans += value[lson[use[x]]];
        x -= x&-x;
    }
    return ans;
}
inline int _sum(int x) {
    int ans = 0;
    while(x) {
        ans += value[use[x]];
        x -= x&-x;
    }
    return ans;
}
inline int query(int R, int x) {
    if(x == 0 || R == 0) return 0;
    for (int i = R; i; i -= i&-i) use[i] = bit[i];
    int l = 1, r = 1000000, m = l+r >> 1, ans = 0;
    int rt = root[R];
    while(l <= r) {
        if(r == x) {
            ans += _sum(R);
            ans += value[rt];
            break;
        }
        if(m > x) {
            r = m;
            for (int i = R; i; i -= i&-i) use[i] = lson[use[i]];
            rt = lson[rt];
        }
        else if(m == x) {
            ans += sum(R);
            ans += value[lson[rt]];
            break;
        } 
        else {
            ans += sum(R);
            ans += value[lson[rt]];
            l = m+1;
            for (int i = R; i; i -= i&-i) use[i] = rson[use[i]];
            rt = rson[rt];
        }
        m = l+r >> 1;
    }
    return ans;
}
int main() {
    build(root[0], 1, 1000000);
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; ++i) update(root[i-1], root[i], a[i], 1, 1, 1000000);
    scanf("%d", &m);
    for (int i = 0; i < m; ++i) {
        scanf("%d", &op);
        if(op == 1) {
            scanf("%d %d", &x, &y);
            add(x, a[x], -1);
            a[x] = y;
            add(x, a[x], 1);
        }
        else {
            scanf("%d %d %d", &l, &r, &p);
            printf("%d ", query(r, a[p]-1) - query(l-1, a[p]-1) +1);
        }
    }
    printf("\n");
    return 0;
}
View Code

正解:分块,块大小用基本不等式求

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define fi first 
#define se second
#define LL long long

const int N = 1e5 + 5;
int n, m, a[N], bl[N], block[N], A, B;
struct Node {
    int op, x, y, p;
}Q[N*2];
int BLOCK;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    scanf("%d", &m);
    for (int i = 1; i <= m; ++i) {
        scanf("%d %d %d", &Q[i].op, &Q[i].x, &Q[i].y);
        if(Q[i].op == 2) scanf("%d", &Q[i].p), ++B;
        else ++A;
    }
    if(A == 0) BLOCK = sqrt(n) + 1;
    else BLOCK = sqrt(B*10LL*n / A) + 1;
    for (int i = 1; i <= n; ++i) {
        bl[i] = (i-1)/BLOCK + 1;
        block[i] = a[i];
    }
    for (int i = 1; i <= bl[n]; ++i) {
        int l = (i-1)*BLOCK + 1;
        int r = min(i*BLOCK, n);
        sort(block+l, block+r+1);
    }
    for (int i = 1; i <= m; ++i) {
        if(Q[i].op == 1) {
            int id = bl[Q[i].x];
            int l = (id-1)*BLOCK + 1;
            int r = min(id*BLOCK, n);
            for (int j = l; j <= r; ++j) {
                if(block[j] == a[Q[i].x]) {
                    block[j] = Q[i].y;
                    int k = j;
                    while(k+1 <= r && block[k+1] < block[k]) {
                        swap(block[k], block[k+1]);
                        ++k;
                    }
                    k = j;
                    while(k-1 >= l && block[k-1] > block[k]) {
                        swap(block[k], block[k-1]);
                        --k;
                    }
                    break;
                }
            }
            a[Q[i].x] = Q[i].y;
        }
        else {
            int cnt = 1;
            if(bl[Q[i].x] == bl[Q[i].y]) {
                for (int j = Q[i].x; j <= Q[i].y; ++j) if(a[j] < a[Q[i].p]) ++cnt;
            }
            else {
                for (int j = Q[i].x; j <= bl[Q[i].x]*BLOCK; ++j) if(a[j] < a[Q[i].p]) ++cnt;
                for (int j = bl[Q[i].x]+1; j <= bl[Q[i].y]-1; ++j) {
                    int l = (j-1)*BLOCK + 1;
                    int r = j*BLOCK;
                    cnt += lower_bound(block+l, block+r+1, a[Q[i].p]) - block - l;
                }
                for (int j = (bl[Q[i].y]-1)*BLOCK+1; j <= Q[i].y; ++j) if(a[j] < a[Q[i].p]) ++cnt;
            }
            printf("%d ", cnt);
        }
    }
    return 0;
}
View Code

 转换成三维偏序问题,用cdq分治求解

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 200005;

struct Node {
    int op, x, y, v, qid;
};
Node a[N * 3], b[N * 3];
int ans[N];
int c[100005];
int sum(int x) {
    int res = 0;
    while (x > 0) {
        res += c[x];
        x -= x & -x;
    }
    return res;
}
void add(int x, int d) {
    while (x <= 100000) {
        c[x] += d;
        x += x & -x;
    }
}
void solve(int l, int r) {
    if (l == r) {
        return;
    }
    int mid = (l + r) / 2;
    solve(l, mid);
    solve(mid + 1, r);
    int p1 = l, p2 = mid + 1, len = r - l + 1;
    for (int i = 0; i < len; i++) {
        if (p1 <= mid && (p2 > r || a[p1].x <= a[p2].x)) {
            b[i] = a[p1++];
            if (b[i].op == 1) {
                add(b[i].y, b[i].v);
            }
        } else {
            b[i] = a[p2++];
            if (b[i].op == 2) {
                ans[b[i].qid] += b[i].v * sum(b[i].y);
            }
        }
    }
    for (int i = l; i <= mid; i++) {
        if (a[i].op == 1) {
            add(a[i].y, -a[i].v);
        }
    }
    for (int i = 0; i < len; i++) {
        a[l + i] = b[i];
    }
}

int w[N];

int main() {
    int n, q;
    scanf("%d", &n);
    int m = 0, qid = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &w[i]);
        a[m++] = {1, w[i], i, 1, -1};
    }
    scanf("%d", &q);
    while (q--) {
        int op;
        scanf("%d", &op);
        if (op == 1) {
            int x, v;
            scanf("%d%d", &x, &v);
            a[m++] = {1, w[x], x, -1, -1};
            w[x] = v;
            a[m++] = {1, w[x], x, 1, -1};
        } else {
            int x, y, v;
            scanf("%d%d%d", &x, &y, &v);
            ++qid;
            a[m++] = {2, w[v] - 1, x - 1, -1, qid};
            a[m++] = {2, w[v] - 1, y, 1, qid};
        }
    }
    solve(0, m - 1);
    for (int i = 1; i <= qid; i++) {
        printf("%d%c", ans[i] + 1, " \n"[i == qid]);
    }
    return 0;
}
View Code

 

posted @ 2018-08-03 16:22  Wisdom+.+  阅读(249)  评论(0编辑  收藏  举报