静态区间第k大
/*
主席树的本质就是动态开点,维护前缀的信息,从而实现了维护多棵线段树。
保存新版本时,复制上一个版本,然后附加上需要修改的儿子,空间为log
静态区间第k大
维护n个权值线段树,第i个权值线段树表示[1...i]这些数构成的权值线段树
对于第i个权值线段树,只需要用第i-1的根,再维护那个改变的值,所用的空间为log
查询区间第k小时用R的权值线段树减去L-1的权值线段树即可
*/
//静态区间第k大
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 2e5+5;
struct node{
int l,r,sum; //权值线段树,左右儿子及总和
}hjt[maxn*40];
int a[maxn],root[maxn],cnt;
vector<int> v;
int get_id(int x)
{
return lower_bound(v.begin(),v.end(),x) - v.begin() + 1; //离散化,下标从1开始
}
void insert(int l,int r,int pre,int &now,int p) //当前区间为[l,r],当前的节点为now,pre为前一个节点,插入p
{
hjt[++cnt] = hjt[pre]; //使用新节点,将前一个节点复制过来
now = cnt; //需要修改now值,所以传引用
hjt[now].sum ++; //增加一个新元素sum值++
if( l == r ) return;
int m = ( l + r ) >> 1;
if( p <= m ) insert(l,m,hjt[pre].l,hjt[now].l,p); //要插入的元素在左区间,意味着右区间now与pre其实是相等的
else insert(m+1,r,hjt[pre].r,hjt[now].r,p);
}
int query(int l,int r,int L,int R,int k) //当前区间为[l,r],要查询的区间的左右端点为L和R,查询第k大
{
if( l == r ) return l; //左右区间相等,只剩一个数了
int m = (l + r) >> 1;
int tmp = hjt[hjt[R].l].sum - hjt[hjt[L].l].sum; //计算左子树的元素个数
if( k <= tmp ) return query(l,m,hjt[L].l,hjt[R].l,k);
else return query(m+1,r,hjt[L].r,hjt[R].r,k-tmp);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> 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++)
{
insert(1,n,root[i-1],root[i],get_id(a[i])); //插入离散化后的值
}
for (int i = 1; i <= m; i++)
{
int l,r,k;
cin >> l >> r >> k;
cout << v[query(1,n,root[l-1],root[r],k)-1] << '\n';
}
return 0;
}
主席树的区间修改
单点修改时,直接跑到叶子节点,修改的节点只有log个,但是区间修改的话如果跑到叶子肯定是不行的,懒标记的存在使得下传时存在问题,一种操作是下传时新建节点,由于操作只有nlogn,新增的点数量也不会太多,就是空间不大好。但是其实有些情况是可以在访问时维护这个值,比如维护区间极值,每次修改的值都比之前的大,那么我们就不用下传标记,直接在访问的过程中维护懒标记的极值。
动态区间第k大
/*
支持单点修改的区间第k大
主席树套树状数组
静态区间第k大,每棵主席树维护的是前缀的权值线段树
现在由于需要修改,所以我们需要将树状数组的思想引入,log修改单点,log查询前缀和
第i棵线段树维护[i-lowbit(i),i]这一区间的权值线段树,修改时改log次即可,查询时求和log棵线段树
时间复杂度n(logn)^2,不离散化的话有一个log是log1e9,但是方便写很多
*/
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
struct node{
int l,r;
int sum;
}hjt[maxn*400]; //每次修改是log次,所以空间需要开大一些,极限情况下是log1e5*log1e9,由于动态开点所以没有离散化
int root[maxn],a[maxn],cnt;
int n;
void insert(int l,int r,int pre,int &now,int p,int v) //当前区间为[l,r],p位置加上v
{
if( !now ) now = ++cnt; //如果now为0才需要给新的节点,节约空间,动态开点时都这么写!!!
hjt[now] = hjt[pre];
hjt[now].sum += v;
if( l == r ) return;
int m = ( l + r ) >> 1;
if( p <= m ) insert(l,m,hjt[pre].l,hjt[now].l,p,v);
else insert(m+1,r,hjt[pre].r,hjt[now].r,p,v);
}
int lowbit(int x)
{
return x & -x;
}
void update(int x,int pos,int v)
{
for (int i = x; i <= n; i += lowbit(i))
{
insert(0,1e9,root[i],root[i],pos,v); //类比树状数组的操作,那时候的c[i]数组,这时候的第i棵线段树
}
}
vector<int> id[2]; //当前访问的左右区间对应的线段树节点的编号
int cal(int index,int flag) //计算某一子树的前缀和
{
int res = 0;
for (int i = 0; i < id[index].size(); i++)
{
if( flag == 0 ) res += hjt[hjt[id[index][i]].l].sum;
else res += hjt[hjt[id[index][i]].r].sum;
}
return res;
}
int query(int l,int r,int k) //当前区间为[l,r],查询第k小
{
if( l == r ) return l; //左右区间相等,只剩一个数了
int m = (l + r) >> 1;
int tmp = cal(0,0) - cal(1,0); //计算左子树的元素个数
if( k <= tmp )
{
for (int i = 0; i < 2; i++) //所有的前缀节点都要变为其左儿子
{
for (int j = 0; j < id[i].size(); j++) id[i][j] = hjt[id[i][j]].l;
}
return query(l,m,k);
}
else
{
for (int i = 0; i < 2; i++) //所有的前缀节点都要变为其右儿子
{
for (int j = 0; j < id[i].size(); j++) id[i][j] = hjt[id[i][j]].r;
}
return query(m+1,r,k-tmp);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++)
{
update(i,a[i],1);
}
for (int i = 1; i <= m; i++)
{
char op;
cin >> op;
if( op == 'Q' )
{
int l,r,k;
cin >> l >> r >> k;
id[0].clear();
id[1].clear();
for (int j = r; j > 0; j -= lowbit(j) ) id[0].push_back(root[j]);
for (int j = l-1; j > 0; j -= lowbit(j) ) id[1].push_back(root[j]);
//这里十分关键!!!由于我们在查询第k小的过程中需要查找线段树上某一区间的前缀和,
//但是我们并不知道这些前缀的节点编号,所以我们需要维护树状数组求和的过程中用到的节点,
//在去其子树的时候一起更新
cout << query(0,1e9,k) << '\n';
}else
{
int x,y;
cin >> x >> y;
update(x,a[x],-1);
update(x,y,1);
a[x] = y;
}
}
return 0;
}