先贴模板 后代码解释
线上U盘,哈哈哈哈
静态主席树:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <stack>
#define Clear( x , y ) memset( x , y , sizeof(x) );
#define Qcin() std::ios::sync_with_stdio(false);
using namespace std;
typedef long long LL;
const int Maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
int N , M;
int cnt = 0, root[Maxn] , a[Maxn];
struct node{
int l , r , sum;
}T[Maxn*40];
vector <int> v;
void init(){
cnt = 0;
T[cnt].l=0, T[cnt].r=0 , T[cnt].sum=0;
root[cnt] = 0;
v.clear();
}
int getid(int x){
return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;
}
void updata(int l,int r, int &x , int y , int pos){
T[++cnt]=T[y] , T[cnt].sum++ , x=cnt;
if(l==r) return;
int mid = (l+r) /2;
if(mid >= pos) updata(l,mid , T[x].l , T[y].l , pos);
else updata(mid+1,r , T[x].r , T[y].r , pos);
}
int query(int l,int r , int x , int y , int k){
if(l==r) return l;
int mid = (l+r) /2;
int sum = T[T[y].l].sum - T[T[x].l].sum;
if(sum >= k) return query(l,mid, T[x].l , T[y].l , k);
else return query(mid+1,r , T[x].r , T[y].r , k-sum);
}
int main()
{
init();
scanf(" %d %d",&N,&M);
for(int i=1 ; i <= N ; i++){
scanf(" %d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin() , v.end());
v.erase(unique(v.begin() , v.end()) , v.end());
for(int i=1;i<=N;i++){
updata(1,N , root[i] , root[i-1] , getid(a[i]));
}
while(M--){
int x , y , k;
scanf(" %d %d %d",&x,&y,&k);
int ans = v[query(1,N, root[x-1] , root[y] , k)-1];
printf("%d\n",ans);
}
return 0;
}
带修主席树
模板:
struct Node {
int l, r, k, f;
}q[MaxQ];
int root[MaxN], lson[MaxN * 40], rson[MaxN * 40], sum[MaxN * 40];
int n, m, tot, cnt, idx;
int a[MaxN], cpy[MaxN];
int S[MaxN], use[MaxN];
int build(int l, int r) {
int rt = tot++; sum[rt] = 0;
if(l != r) {
int mid = (l + r) >> 1;
lson[rt] = build(l, mid);
rson[rt] = build(mid + 1, r);
}
return rt;
}
int update(int last, int pos, int val) {
int rt = tot++, tmp = rt, l = 0, r = cnt - 1;
sum[rt] = sum[last] + val;
while(l < r) {
int mid = (l + r) >> 1;
if(pos <= mid) {
lson[rt] = tot++; rson[rt] = rson[last];
rt = lson[rt]; last = lson[last];
r = mid;
}
else {
rson[rt] = tot++; lson[rt] = lson[last];
rt = rson[rt]; last = rson[last];
l = mid + 1;
}
sum[rt] = sum[last] + val;
}
return tmp;
}
int lowbit(int x) { return x & (-x); }
void add(int p, int pos, int val) {
for(int i = p; i <= n; i += lowbit(i)) S[i] = update(S[i], pos, val);
}
int getSum(int p) {
int ans = 0;
for(int i = p; i > 0; i -= lowbit(i)) ans += sum[lson[use[i]]];
return ans;
}
int query(int L, int R, int k) {
int l_root = root[L-1], r_root = root[R];
int l = 0, r = cnt-1;
for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = S[i];
for(int i = R; i > 0; i -= lowbit(i)) use[i] = S[i];
while(l < r) {
int mid = (l + r) >> 1;
int tmp = getSum(R) - getSum(L-1) + sum[lson[r_root]] - sum[lson[l_root]];
if(tmp >= k) {
r = mid;
for(int i = L-1; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
for(int i = R; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
l_root = lson[l_root], r_root = lson[r_root];
}
else {
l = mid + 1, k -= tmp;
for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
for(int i = R; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
l_root = rson[l_root], r_root = rson[r_root];
}
}
return l;
}
int getId(int x) { return lower_bound(cpy, cpy + cnt, x) - cpy; }
int main() {
tot = cnt = idx = 0;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
cpy[idx++] = a[i];
}
for(int i = 0; i < m; i++) {
char op[10]; scanf("%s", op);
if(op[0] == 'Q') {
q[i].f = 0;
scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].k);
}
else {
q[i].f = 1;
scanf("%d %d", &q[i].l, &q[i].r);
cpy[idx++] = q[i].r;
}
}
sort(cpy, cpy + idx);
cnt = unique(cpy, cpy + idx) - cpy;
root[0] = build(0, cnt - 1);
for(int i = 1; i <= n; i++) root[i] = update(root[i-1], getId(a[i]), 1);
for(int i = 1; i <= n; i++) S[i] = root[0];
for(int i = 0; i < m; i++) {
if(q[i].f == 0) printf("%d\n", cpy[query(q[i].l, q[i].r, q[i].k)]);
else {
add(q[i].l, getId(a[q[i].l]), -1);
add(q[i].l, getId(q[i].r), 1);
a[q[i].l] = q[i].r;
}
}
}
代码解释
// BZOJ 1901
struct Node {
int l, r, k, f;
}q[MaxQ];
int root[MaxN], lson[MaxN * 40], rson[MaxN * 40], sum[MaxN * 40];
int n, m, tot, cnt, idx;
int a[MaxN], cpy[MaxN];
int S[MaxN], use[MaxN];
int build(int l, int r) {
int rt = tot++; sum[rt] = 0;
if(l != r) {
int mid = (l + r) >> 1;
lson[rt] = build(l, mid);
rson[rt] = build(mid + 1, r);
}
return rt;
}
int update(int last, int pos, int val) {
//新节点 tot , 和二分区间 l r
int rt = tot++, tmp = rt, l = 0, r = cnt - 1;
sum[rt] = sum[last] + val;
while(l < r) {
int mid = (l + r) >> 1;
if(pos <= mid) {
lson[rt] = tot++; rson[rt] = rson[last];
rt = lson[rt]; last = lson[last];
r = mid;
}
else {
rson[rt] = tot++; lson[rt] = lson[last];
rt = rson[rt]; last = rson[last];
l = mid + 1;
}
sum[rt] = sum[last] + val;
}
//tmp = tot
return tmp;
}
int lowbit(int x) { return x & (-x); }
void add(int p, int pos, int val) {
//因为 树状数组来把 对于 pos ~ n 后面的位置用树状数组更新 O(log2[n])
for(int i = p; i <= n; i += lowbit(i)) S[i] = update(S[i], pos, val);
}
int getSum(int p) {
//整体下来就是 1 ~ p 的前缀和 (线段树所记录的修改部分)
int ans = 0;
//相当于线段树的查询操作 use[i]节点 的左儿子 的前缀和
for(int i = p; i > 0; i -= lowbit(i)) ans += sum[lson[use[i]]];
return ans;
}
int query(int L, int R, int k) {
int l_root = root[L-1], r_root = root[R];
int l = 0, r = cnt-1;
//use[] 数组 作为可变化的S[] 的查询数组
for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = S[i];
for(int i = R; i > 0; i -= lowbit(i)) use[i] = S[i];
while(l < r) {
int mid = (l + r) >> 1;
//查询getsum部分是线段树的前缀和, 后面sum是原主席树的查询部分
int tmp = getSum(R) - getSum(L-1) + sum[lson[r_root]] - sum[lson[l_root]];
if(tmp >= k) {
//当 前缀部分 >= k ,我们就可以直接更新use数组为 它的下一层lson
r = mid;
for(int i = L-1; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
for(int i = R; i > 0; i -= lowbit(i)) use[i] = lson[use[i]];
l_root = lson[l_root], r_root = lson[r_root];
}
else {
//当 前缀部分 < k ,我们就可以直接更新use数组为 它的下一层rson
l = mid + 1, k -= tmp;
for(int i = L - 1; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
for(int i = R; i > 0; i -= lowbit(i)) use[i] = rson[use[i]];
l_root = rson[l_root], r_root = rson[r_root];
}
}
return l;
}
int getId(int x) { return lower_bound(cpy, cpy + cnt, x) - cpy; }
int main() {
tot = cnt = idx = 0;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
cpy[idx++] = a[i];
}
for(int i = 0; i < m; i++) {
char op[10]; scanf("%s", op);
if(op[0] == 'Q') {
// 代表查询操作
q[i].f = 0;
scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].k);
}
else {
//代表修改操作
q[i].f = 1;
scanf("%d %d", &q[i].l, &q[i].r);
cpy[idx++] = q[i].r;
}
}
// cpy数组要包含 修改后的值, 因为离散化要把所有的数字都进行hash
sort(cpy, cpy + idx);
cnt = unique(cpy, cpy + idx) - cpy;
//建立一个权值全部为0 的线段树
//用一个权值线段树,模拟主席树的修改部分
root[0] = build(0, cnt - 1);
//把原数组a[] 像静态主席树一样逐个插入到 主席树中
for(int i = 1; i <= n; i++) root[i] = update(root[i-1], getId(a[i]), 1);
//未更新的 线段树root 初始化
for(int i = 1; i <= n; i++) S[i] = root[0];
for(int i = 0; i < m; i++) {
//查询操作
if(q[i].f == 0) printf("%d\n", cpy[query(q[i].l, q[i].r, q[i].k)]);
else {
//更新操作
//删掉 原值
add(q[i].l, getId(a[q[i].l]), -1);
//更新 新值
add(q[i].l, getId(q[i].r), 1);
a[q[i].l] = q[i].r;
}
}
}