Dynamic Rankings
这道题是带修主席树的板子题。我们先来考虑一下主席树带修改最暴力的做法,就是暴力修改与之有关的所有权值线段树,这样的话单次操作的复杂度就是\(O(nlogn)\)的,总体的操作就是\(O(n^2logn)\)的,显然吃不消。
因为主席树其实应用的是前缀和的思想,我们考虑一下在最开始的时候,我们是怎么维护单点修改的前缀和的?使用树状数组。在普通的主席树里我们用的是差分的思想,但是现在我们不用差分了,我们用树状数组的思想(有人说叫套一个树状数组,但是其实我觉得没有实际套上……怎么说都行),使得根节点为\(root_x\)的主席树维护的是\(lowbit_x\)区间之内的权值,修改的时候像树状数组一样修改,查询的时候也一样查询,之后就和普通的主席树很相似了。
在实现上还是有很多细节的。修改和建树不难,因为这时不需要考虑继承上一个根结点的信息,直接修改就可以。不过在查询的时候,我们不能先进入树中之后再调用\(lowbit\)进行循环查询,因为进入树中之后我们就不知道下一步往哪走了。所以解决办法是一开始先把\(l-1\)和\(r\)能访问到的区间都跑出来,把他们的根节点存在数组里,之后在询问的时候,同步的把所有根节点都向其左/右儿子移动就可以了,顺便使用这个来统计答案,确定应该往哪边走。
这样的话这道题修改是\(O(log^2n)\),总复杂度为\(O(nlog^2n)\),可以通过。
还有就是此题实际上不需要离散化,因为主席树上权值只是用来帮你判断应该往哪走,这题因为没要求强制在线,所以可以像我一样,先离线把所有的都存下来,之后进行离散化之后再操作,也可以不离散直接操作,但是这样二分的边界会大一点。
我自己试了一下,对于普通的主席树,其实离不离散对于答案的正确性影响不大,但是如果不离散,那么二分的范围必须开到上下界,也就是稳定的\(O(nlogmax_{val})\),但是离散化以后就会好很多。不离散的话会浪费时间和空间,不过其实也不会太多,一般会多几个常数的\(O(n)\)复杂度,差的不是很多。比如这题离不离散只差了1000ms(一共14000ms)。
所以离不离散的话可能ssy说的对:“你觉得不离散过不去的时候。”
看一下代码。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
const int M = 400005;
const int N = 30000005;
int read()
{
int ans = 0,op = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') op = -1;
ch = getchar();
}
while(ch >='0' && ch <= '9')
{
ans *= 10;
ans += ch - '0';
ch = getchar();
}
return ans * op;
}
struct node
{
int ls,rs,v;
}t[N];
int a[M],b[M],n,m,x[M],y[M],z[M],c[M],idx,t1[M],t2[M],cnt1,cnt2,root[M],tot;
char s[2];
void modify(int &p,int l,int r,int pos,int val)
{
if(!p) p = ++idx;
t[p].v += val;
if(l == r) return;
int mid = (l+r) >> 1;
if(pos <= mid) modify(t[p].ls,l,mid,pos,val);
else modify(t[p].rs,mid+1,r,pos,val);
}
void change(int pos,int val)
{
int x = pos;
while(x <= n) modify(root[x],1,tot,a[pos],-1),x += lowbit(x);
a[pos] = val,x = pos;
while(x <= n) modify(root[x],1,tot,a[pos],1),x += lowbit(x);
}
int query(int l,int r,int k)
{
if(l == r) return l;
int sum = 0,mid = (l+r) >> 1;
rep(i,1,cnt1) sum -= t[t[t1[i]].ls].v;
rep(i,1,cnt2) sum += t[t[t2[i]].ls].v;
if(k <= sum)
{
rep(i,1,cnt1) t1[i] = t[t1[i]].ls;
rep(i,1,cnt2) t2[i] = t[t2[i]].ls;
return query(l,mid,k);
}
else
{
rep(i,1,cnt1) t1[i] = t[t1[i]].rs;
rep(i,1,cnt2) t2[i] = t[t2[i]].rs;
return query(mid+1,r,k - sum);
}
}
int main()
{
n = read(),m = read();
rep(i,1,n) a[i] = b[i] = read();
rep(i,1,m)
{
scanf("%s",s);
if(s[0] == 'Q') x[i] = read(),y[i] = read(),z[i] = read();
else c[i] = read(),a[i+n] = b[i+n] = read();
}
sort(b+1,b+n+m+1);
tot = unique(b+1,b+1+n+m) - b - 1;
rep(i,1,n+m) a[i] = lower_bound(b+1,b+1+tot,a[i]) - b;
rep(i,1,n)
{
int j = i;
while(j <= n) modify(root[j],1,tot,a[i],1),j += lowbit(j);
}
rep(i,1,m)
{
if(!x[i]) {change(c[i],a[i+n]);continue;}
int j = x[i] - 1,k = y[i];
cnt1 = cnt2 = 0;
while(j) t1[++cnt1] = root[j],j -= lowbit(j);
while(k) t2[++cnt2] = root[k],k -= lowbit(k);
printf("%d\n",b[query(1,tot,z[i])]);
}
return 0;
}