https://www.luogu.com.cn/problem/P2617
给定一个含有 n n n 个数的序列 a 1 , a 2 … a n a_1,a_2 \dots a_n a1,a2…an,需要支持两种操作:
Q l r k
表示查询下标在区间 [ l , r ] [l,r] [l,r] 中的第 k k k 小的数C x y
表示将 a x a_x ax 改为 y y y第一行两个正整数 n , m n,m n,m,表示序列长度与操作个数。
第二行 n n n 个整数,表示 a 1 , a 2 … a n a_1,a_2 \dots a_n a1,a2…an。
接下来 m m m 行,每行表示一个操作,都为上述两种中的一个。
对于每一次询问,输出一行一个整数表示答案。
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
3
6
【数据范围】
对于 10 % 10\% 10% 的数据, 1 ≤ n , m ≤ 100 1\le n,m \le 100 1≤n,m≤100;
对于 20 % 20\% 20% 的数据, 1 ≤ n , m ≤ 1000 1\le n,m \le 1000 1≤n,m≤1000;
对于 50 % 50\% 50% 的数据, 1 ≤ n , m ≤ 1 0 4 1\le n,m \le 10^4 1≤n,m≤104;
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 1 0 5 1\le n,m \le 10^5 1≤n,m≤105, 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n, 1 ≤ k ≤ r − l + 1 1 \le k \le r-l+1 1≤k≤r−l+1, 1 ≤ x ≤ n 1\le x \le n 1≤x≤n, 0 ≤ a i , y ≤ 1 0 9 0 \le a_i,y \le 10^9 0≤ai,y≤109。
请注意常数优化,但写法正常的整体二分和树套树都可以以大约 1000 ms 1000\text{ms} 1000ms 每个点的时间通过。
来源:bzoj1901
本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。
动态维护区间 k k k小 / / /大值。
我们做过静态的区间 k k k小 / / /大值,是采用前缀和的思想建前缀树,即主席树来解的。那么动态时,当更新节点时,我们需要改每颗树上的 l o g log log个节点,单次复杂度达到 n l o g n nlogn nlogn,显然不可取。
那么我们同样看看,用前缀和的思想,能不能像权值线段树那样,模仿主席树那样跳节点找 k k k小呢 ? ? ?
此时我们的外层就不在是主席树那样简单的 i − > i + 1 i->i+1 i−>i+1的前缀方式了,这样会导致 n n n棵树互相关联;
我们不想让他们关联,要方便修改,那么前缀就要换一种方式。
此时就可以套上树状数组了。我们在树状数组的每个节点,代表的那一段区间里,开一个权值线段树,然后树状数组每个节点记录下此节点权值线段树里,数的个数,到时候我们查询时,就像主席树那样,用 1 1 1到 r r r的总区间减去 1 1 1到 l − 1 l-1 l−1总区间,即得到 l l l到 r r r的权值情况。然后,树状数组里 1 1 1到 r r r的总区间可能包含多个节点才能凑出来,所以不像主席树是两棵树在跳,这里是 2 ∗ l o g n 2*logn 2∗logn棵树一起跳,因为他们身上都有必要的前缀和信息。
所以树状数组的作用是保存的是该区间值域内数的个数,方便查询;每个树状数组节点下的权值线段树的作用是维护该区间值域内数的个数,方便修改。
复杂度就不说了,大佬那里说的很清楚。
/*keep on going and never give up*/
#include
using namespace std;
#define int long long
#define ll long long
#define db(x) cerr<<(#x)<<" "<<(x)<<" "<<endl;
#define endl "\n"
#define INF 1e17
#define fast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double E = exp(1);
const double PI = acos(-1.0);
const int maxn=2e5+10;
struct sgt{
int v,ls,rs;
}t[maxn*100];
struct dd{
bool b;
int l,r,k;
int pos,t;
}q[maxn];
int n,m,a[maxn],id[maxn<<1],rt[maxn],len,tot,temp[2][20],cnt[2];
char op;
void update(int &now,int l,int r,int pos,int val){
if (!now) now=++tot;
t[now].v+=val;
if (l==r) return;
int mid=l+r>>1;
if (pos<=mid) update(t[now].ls,l,mid,pos,val);
else update(t[now].rs,mid+1,r,pos,val);
}
void _update(int x,int val){
int k=lower_bound(id+1,id+len+1,a[x])-id;
for (int i=x;i<=n;i+=i&-i) update(rt[i],1,len,k,val);
}
int ask(int l,int r,int k){
if (l==r) return l;
int mid=l+r>>1,sum=0;
for (int i=1;i<=cnt[1];i++) sum+=t[t[temp[1][i]].ls].v;
for (int i=1;i<=cnt[0];i++) sum-=t[t[temp[0][i]].ls].v;
if (k<=sum){
for (int i=1;i<=cnt[1];i++) temp[1][i]=t[temp[1][i]].ls;
for (int i=1;i<=cnt[0];i++) temp[0][i]=t[temp[0][i]].ls;
return ask(l,mid,k);
}
else{
for (int i=1;i<=cnt[1];i++) temp[1][i]=t[temp[1][i]].rs;
for (int i=1;i<=cnt[0];i++) temp[0][i]=t[temp[0][i]].rs;
return ask(mid+1,r,k-sum);
}
}
int _ask(int l,int r,int k){
memset(temp,0,sizeof(temp));
cnt[0]=cnt[1]=0;
for (int i=r;i;i-=i&-i) temp[1][++cnt[1]]=rt[i];
for (int i=l-1;i;i-=i&-i) temp[0][++cnt[0]]=rt[i];
return ask(1,len,k);
}
signed main(){
fast
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i],id[++len]=a[i];
for (int i=1;i<=m;i++){
cin>>op;
q[i].b=(op=='Q');
if (q[i].b) cin>>q[i].l>>q[i].r>>q[i].k;
else cin>>q[i].pos>>q[i].t,id[++len]=q[i].t;
}
sort(id+1,id+len+1);
len=unique(id+1,id+len+1)-id-1;
for (int i=1;i<=n;i++) _update(i,1);
for (int i=1;i<=m;i++){
if (q[i].b) printf("%d\n",id[_ask(q[i].l,q[i].r,q[i].k)]);
else{
_update(q[i].pos,-1);
a[q[i].pos]=q[i].t;
_update(q[i].pos,1);
}
}
}