qwq
菜的真实
过完年一点都不想学习。
首先我们看这个题,一个带修主席树的板子题。
qwq
首先,如果选择暴力修改,每次会修改
O
(
n
)
O(n)
O(n)级别的主席树,这个复杂度是没有办法接受的。
但是我们学过一个东西叫树状数组
B
I
T
BIT
BIT
他的思想,是将修改和查询的复杂度都变成了
l
o
g
log
log
相当于是带修前缀和。
既然主席树也是前缀和的形式。
那么这时候呢,我们不妨稍微改变一下主席树的构造方式。
也就是说,牺牲一些查询前缀和的速度,但是加快我们修改的速度。
考虑对于一颗主席树 r o o t [ i ] root[i] root[i]维护的是 [ i − l o w b i t ( i ) + 1 , i ] [i-lowbit(i)+1,i] [i−lowbit(i)+1,i]的和,那么对于构造主席树的时候呢,对于一个位置来说,需要修改log棵的主席树。
可以理解成一个树状数组结构的主席树
void add(int pos,int x,int p)
{
int val = getnum(x);
for (int i=pos;i<=n;i+=lowbit(i))
root[i]=modify(root[i],1,sz,val,p);
}
那这样修改的问题就迎刃而解了
首先把原来权值的数量-1,然后新的权值的数量+1
那么应该怎么查询呢?
qwq由于我们把修改的速度变快了,所以查询的速度也要相应的变慢一些。
考虑将 [ l , r ] [l,r] [l,r]转化成 [ 1 , r ] − [ 1 , l − 1 ] [1,r] - [1,l-1] [1,r]−[1,l−1]
那么我们首先把 l − 1 和 r l-1和r l−1和r进行拆分,拆分成log个 r o o t root root的形式,因为我们维护的主席树就是一段一段的
for (int j=q[i].x-1;j;j-=lowbit(j)) l[++num1]=root[j]; //分别把左右区间进行拆解
for (int j=q[i].y;j;j-=lowbit(j)) r[++num2]=root[j];
q u e r y query query的时候呢,对于左端点拆出来的端点的 r o o t root root的 v a l val val是减,右端点是加。
如果要进入左子树,那么所有拆出来的部分 都要进入左子树
qwq
感觉比较难说明白
还是看代码会比较好
int query(int lll,int rr,int k)
{
if (lll==rr) return lll;
int mid = lll+rr >> 1;
int vall=0;
for (int i=1;i<=num1;i++) vall-=t[t[l[i]].l].val;
for (int i=1;i<=num2;i++) vall+=t[t[r[i]].l].val; //相当于把区间[l,r]转化成[1,r]-[1,l-1]
if (vall>=k)
{
for (int i=1;i<=num1;i++) l[i]=t[l[i]].l;
for (int i=1;i<=num2;i++) r[i]=t[r[i]].l;
return query(lll,mid,k);
}
else
{
for (int i=1;i<=num1;i++) l[i]=t[l[i]].r;
for (int i=1;i<=num2;i++) r[i]=t[r[i]].r;
return query(mid+1,rr,k-vall);
}
}
那么到这里差不多就结束了
qwq
感觉思路还是很巧妙的
qwq
下面放一下整个题的代码
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
struct Node{
int l,r,val;
};
Node t[maxn*20*10];
int n,m;
struct pp{
int opt;
int x,y,k;
};
pp q[maxn];
int val[maxn],root[maxn];
int a[maxn];
int sz;
vector<int> v;
int l[maxn],r[maxn];
int num1,num2;
int tot;
inline int lowbit(int x)
{
return x&(-x);
}
void up(int root)
{
t[root].val=t[t[root].l].val+t[t[root].r].val;
}
int getnum(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int modify(int pre,int l,int r,int x,int p)
{
int now = ++tot;
if (l==r)
{
t[now].val=t[pre].val+p;
return now;
}
int mid = l+r >> 1;
if (x<=mid)
{
t[now].r=t[pre].r;
t[now].l=modify(t[pre].l,l,mid,x,p);
}
else
{
t[now].l=t[pre].l;
t[now].r=modify(t[pre].r,mid+1,r,x,p);
}
up(now);
return now;
}
void add(int pos,int x,int p)
{
int val = getnum(x);
for (int i=pos;i<=n;i+=lowbit(i))
root[i]=modify(root[i],1,sz,val,p);
}
int query(int lll,int rr,int k)
{
if (lll==rr) return lll;
int mid = lll+rr >> 1;
int vall=0;
for (int i=1;i<=num1;i++) vall-=t[t[l[i]].l].val;
for (int i=1;i<=num2;i++) vall+=t[t[r[i]].l].val; //相当于把区间[l,r]转化成[1,r]-[1,l-1]
if (vall>=k)
{
for (int i=1;i<=num1;i++) l[i]=t[l[i]].l;
for (int i=1;i<=num2;i++) r[i]=t[r[i]].l;
return query(lll,mid,k);
}
else
{
for (int i=1;i<=num1;i++) l[i]=t[l[i]].r;
for (int i=1;i<=num2;i++) r[i]=t[r[i]].r;
return query(mid+1,rr,k-vall);
}
}
int main()
{
n=read();m=read();
for (int i=1;i<=n;i++) val[i]=read(),v.pb(val[i]);
for (int i=1;i<=m;i++)
{
char s[10];
scanf("%s",s+1);
if (s[1]=='Q')
{
q[i].opt=1;
q[i].x=read();
q[i].y=read();
q[i].k=read();
}
else
{
q[i].opt=2;
q[i].x=read();
q[i].y=read();
v.pb(q[i].y);
}
}
sort(v.begin(),v.end());
sz = unique(v.begin(),v.end())-v.begin();
v.resize(sz);
for (int i=1;i<=n;i++) add(i,val[i],1);
//cout<<1<<endl;
for (int i=1;i<=m;i++)
{
if (q[i].opt==1)
{
num1=num2=0;
for (int j=q[i].x-1;j;j-=lowbit(j)) l[++num1]=root[j]; //分别把左右区间进行拆解
for (int j=q[i].y;j;j-=lowbit(j)) r[++num2]=root[j];
// cout<<num1<<" "<<num2<<endl;
int now = query(1,sz,q[i].k)-1;
//cout<<now<<endl;
cout<<v[now]<<"\n";
}
else
{
add(q[i].x,val[q[i].x],-1);
val[q[i].x]=q[i].y;
add(q[i].x,val[q[i].x],1);
}
//cout<<"***"<<endl;
}
return 0;
}