算法笔记--可持久化线段树
参考: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; }
思路:
将求第k小的问题的问题转换成权值统计问题,采用可持久化线段树维护
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
zoj 2112
思路:
树状数组套可持久线段树,注意一开始要建静态主席树,否则会爆栈
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 */
思路:
和上面一样,但空间足够,所以原数组不用开静态主席树
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
蓝桥杯 第几小
思路:
树状数组套可持久线段树,但被卡常了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
正解:分块,块大小用基本不等式求
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
转换成三维偏序问题,用cdq分治求解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }