题目大意:
给你一串数字,然后给你两种操作:
1:1 l v
操作一:把下标为l的点的值替换为v
2:2 l r k
操作二:在[l,r]区间求第k大值!
1.整体二分
对于初始数字,变为插入操作
按操作的时间顺序排列各个操作,对于修改操作拆为删除和加入操作:
1 删除之前插入的数字,2. 加入新的数字
接下来分治二分答案:
对于mid,如果插入或者删除的数字<=mid那么应该放到左区间,并且用树状数组(其他数据结构也行)
维护前X个位置有多少个数字在左边。
对于询问:如果l,r区间在左边的数字>=k那么答案在左边,否则答案在右边,并且更新K,k=k-这个区间去左边的数字个数
当low =high的时候,说明low就是答案了,只要更新询问还在low,low之间的答案即可。
复杂度分析:分治的深度是log(S)s是数据的范围。
每个询问每次被分到左边或者右边,会有log(s)次操作。每个插入或者删除也是。
但是要用树状数组维护多少个数字在左边,要log(n)次更新或者查询。
2.树状数组套平衡树
我们将树状数组的每一个节点代表对应的数字(需要将询问读入,然后把所有出现的数字离散化),平衡树中保存每一个数在序列中的下标。
修改:将原来序列中的数字在对应的树状数组套的平衡树中删除,再同理插入新的数字
询问:考虑答案的二进制表示,通过巧妙地运用树状数组的性质,我们可以从高位往地位贪心的构造答案,每次贪心在平衡树中查找对应区间的数字个数。
修改时间复杂度O(log^2N)
询问时间复杂度O(log^2N)构造答案+树状数组单点查询+平衡树查询
给你一串数字,然后给你两种操作:
1:1 l v
操作一:把下标为l的点的值替换为v
2:2 l r k
操作二:在[l,r]区间求第k大值!
1.整体二分
对于初始数字,变为插入操作
按操作的时间顺序排列各个操作,对于修改操作拆为删除和加入操作:
1 删除之前插入的数字,2. 加入新的数字
接下来分治二分答案:
对于mid,如果插入或者删除的数字<=mid那么应该放到左区间,并且用树状数组(其他数据结构也行)
维护前X个位置有多少个数字在左边。
对于询问:如果l,r区间在左边的数字>=k那么答案在左边,否则答案在右边,并且更新K,k=k-这个区间去左边的数字个数
当low =high的时候,说明low就是答案了,只要更新询问还在low,low之间的答案即可。
复杂度分析:分治的深度是log(S)s是数据的范围。
每个询问每次被分到左边或者右边,会有log(s)次操作。每个插入或者删除也是。
但是要用树状数组维护多少个数字在左边,要log(n)次更新或者查询。
复杂度是n*log(s)*log(n)
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
#define maxn 300007
int tree[maxn];
void add(int p,int n){
for(;p<maxn;p+=p&(-p))
tree[p]+=n;
}
int query(int p){
int ans = 0;
for(;p>0;p-=p&(-p))
ans += tree[p];
return ans;
}
struct Node{
int l,r,k,ty,ans;
};
Node p[maxn];
int id1[maxn],id2[maxn];
void CDQ(int L,int R,int low,int high){
if(R < L) return ;
if(low == high ){
for(;L<=R;L++){
p[id1[L]].ans = low;
}
return ;
}
int mid = (low+high)/2,l=L,r=R,k,u;
for(int i = L;i <= R; i++){
u = id1[i];
if(p[u].ty == 2){
k = query(p[u].r) - query(p[u].l-1);
if(k >= p[u].k) id2[l++] = u;
else {
p[u].k -= k;
id2[r--] = u;
}
}
else if(p[u].k <= mid){
add(p[u].l,p[u].ty);
id2[l++] = u;
}
else id2[r--] = u;
}
for(int i = L; i <= R; i++){
u = id1[i];
if(p[u].ty != 2 && p[u].k <= mid) add(p[u].l,-p[u].ty);
}
for(k=L;k<l;k++)
id1[k] = id2[k];
for(r=R;k<=R;k++)
id1[k] = id2[r--];
CDQ(L,l-1,low,mid);
CDQ(l,R,mid+1,high);
}
int num[maxn];
int main(){
int n,q,t,cnt;
memset(tree,0,sizeof(tree));
while(scanf("%d",&n)!=EOF){
for(cnt=0;cnt<n;cnt++){
scanf("%d",&p[cnt].k);
p[cnt].ty = 1;
p[cnt].l = cnt+1;
num[cnt+1] = p[cnt].k;
}
scanf("%d",&q);
int ty,l,v;
for(int i = 0;i < q; i++,cnt++){
scanf("%d",&p[cnt].ty);
if(p[cnt].ty == 1){
scanf("%d%d",&l,&v);
p[cnt].ty = -1;
p[cnt].k = num[l];
p[cnt].l = l;
cnt++;
num[l] = v;
p[cnt].ty = 1;
p[cnt].k = v;
p[cnt].l = l;
}
else {
scanf("%d%d%d",&p[cnt].l,&p[cnt].r,&p[cnt].k);
}
}
for(int i = 0;i < cnt; i++)
id1[i] = i;
CDQ(0,cnt-1,0,1000000000);
for(int i = 0;i < cnt; i++){
if(p[i].ty == 2) printf("%d\n",p[i].ans);
}
}
return 0;
}
2.树状数组套平衡树
我们将树状数组的每一个节点代表对应的数字(需要将询问读入,然后把所有出现的数字离散化),平衡树中保存每一个数在序列中的下标。
修改:将原来序列中的数字在对应的树状数组套的平衡树中删除,再同理插入新的数字
询问:考虑答案的二进制表示,通过巧妙地运用树状数组的性质,我们可以从高位往地位贪心的构造答案,每次贪心在平衡树中查找对应区间的数字个数。
修改时间复杂度O(log^2N)
询问时间复杂度O(log^2N)构造答案+树状数组单点查询+平衡树查询
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define MAXN 100002
using namespace std;
using namespace __gnu_pbds;
struct query{int t,l,r,k;}q[MAXN];
//保存询问
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>bit[MAXN*2];
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>::iterator it;
//exSTL红黑树,G++ Only,低版本中null_type为null_mapped_type
int num[MAXN],n,ql,qr,k,oper,lsh[MAXN*2],H,ct;
//lsh为离散化数组 ,H为贪心构造答案的最高位
int lowbit(int x){return x&(-x);}
void add(int p,int num){
for(int i=p;i<=ct;i+=lowbit(i))
bit[i].insert(num);
}
//树状数组更新
void del(int p,int num){
for(int i=p;i<=ct;i+=lowbit(i))
bit[i].erase(num);
}
//树状数组删除
int pos(int num){return lower_bound(lsh,lsh+ct,num)-lsh+1;}
// 返回离散化下标
int query(int l,int r,int k){
int num=0;
for(int i=H;i;i>>=1){ //高位往地位贪心
int tmp=num+i;
if(tmp>ct)continue;
int kth=bit[tmp].order_of_key(r+1)-bit[tmp].order_of_key(l);
//平衡树查询
if(kth>=k)continue;
num=tmp,k-=kth;
}
return num;
}
//贪心构造答案
int main(){
int t;
while(scanf("%d",&n)!=EOF){
ct=0;
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
lsh[ct++]=num[i];
}
scanf("%d",&t);
for(int i=0;i<t;i++){
scanf("%d",&q[i].t);
if(q[i].t==1){
scanf("%d%d",&q[i].l,&q[i].k);
lsh[ct++]=q[i].k;
}else scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
}
sort(lsh,lsh+ct);
ct=unique(lsh,lsh+ct)-lsh;
for(int i=1;i<=ct;i++)bit[i].clear();
H=1;
while(H*2<ct)H*=2;
for(int i=0;i<t;i++)if(q[i].t==1)q[i].k=pos(q[i].k);
for(int i=1;i<=n;i++){
num[i]=pos(num[i]);
add(num[i],i);
}
for(int i=0;i<t;i++){
if(q[i].t==1){
del(num[q[i].l],q[i].l);
add(q[i].k,q[i].l);
num[q[i].l]=q[i].k;
}else printf("%d\n",lsh[query(q[i].l,q[i].r,q[i].k)]);
}
}
return 0;
}
3.线段树套treap
//#pragma warning (disable:4786)
//#pragma comment(linker,"/STACK:102400000,102400000") //手动扩栈
//#include <bits/stdc++.h>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <climits>
#include <ctype.h>
#include <queue>
#include <stack>
#include <vector>
#include <utility>
#include <deque>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.00);
//#define PI 3.1415926535897932384626433832795
const double e = exp(1.0);
#define INF 0x3f3f3f3f
//#define INF 1e18
//typedef long long LL;
//typedef __int64 LL;
#define ONLINE_JUDGE
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
#define N 600010
#define M 100010
struct treap
{
int key,wht,count,sz,ch[2];
} tp[N*15];
int tree[N<<1];
int nodecount,root;
int IDX(int l,int r)
{
return l+r | l!=r;
}
void init()
{
tp[0].sz=0;
tp[0].wht=-INF;
nodecount=0;
root=0;
}
void update(int x)
{
tp[x].sz=tp[tp[x].ch[0]].sz+tp[x].count+tp[tp[x].ch[1]].sz;
}
void rotate(int &x,int t)
{
int y=tp[x].ch[t];
tp[x].ch[t]=tp[y].ch[!t];
tp[y].ch[!t]=x;
update(x);
update(y);
x=y;
}
void insert(int &x,int t)
{
if(! x)
{
x=++nodecount;
tp[x].key=t;
tp[x].wht=rand();
tp[x].count=1;
tp[x].ch[0]=tp[x].ch[1]=0;
}
else if(tp[x].key==t) tp[x].count++;
else
{
int k=tp[x].key<t;
insert(tp[x].ch[k],t);
if(tp[x].wht<tp[tp[x].ch[k]].wht) rotate(x,k);
}
update(x);
}
void erase(int &x,int t)
{
if(tp[x].key==t)
{
if(tp[x].count==1)
{
if(! tp[x].ch[0] && ! tp[x].ch[1])
{
x=0;
return;
}
rotate(x,tp[tp[x].ch[0]].wht<tp[tp[x].ch[1]].wht);
erase(x,t);
}
else tp[x].count--;
}
else erase(tp[x].ch[tp[x].key<t],t);
update(x);
}
int select(int x,int t)
{
if(! x) return 0;
if(tp[x].key>t) return select(tp[x].ch[0],t);
return tp[x].count+tp[tp[x].ch[0]].sz+select(tp[x].ch[1],t);
}
int a[N],b[N],ord[M][5],lb;
int n,m,tt;
int search(int x)
{
int l=1,r=b[0],mid;
while (l<=r)
{
mid=(l+r)>>1;
if(b[mid]==x) return mid;
if(b[mid]<x) l=mid+1;
else r=mid-1;
}
}
void treeinsert(int l,int r,int i,int x)
{
insert(tree[IDX(l,r)],x);
if(l==r) return;
int m=(l+r)>>1;
if(i<=m) treeinsert(l,m,i,x);
else treeinsert(m+1,r,i,x);
}
void treedel(int l,int r,int i,int x)
{
erase(tree[IDX(l,r)],x);
if(l==r) return;
int m=(l+r)>>1;
if(i<=m) treedel(l,m,i,x);
else treedel(m+1,r,i,x);
}
int query(int l,int r,int x,int y,int k)
{
if(l==r) return l;
int m=(l+r)>>1;
int ans=select(tree[IDX(l,m)],y)-select(tree[IDX(l,m)],x);
if(ans>=k) return query(l,m,x,y,k);
return query(m+1,r,x,y,k-ans);
}
int main ()
{
while (~scanf("%d",&n))
{
b[0]=1;
lb=0;
memset(tree,0,sizeof(tree));
init();
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
b[++lb]=a[i];
}
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
int op;
int x,y,c;
scanf("%d",&op);
if(op == 2)
{
scanf("%d %d %d",&x,&y,&c);
ord[i][1]=1;
ord[i][2]=x;
ord[i][3]=y;
ord[i][4]=c;
}
else
{
scanf("%d %d",&x,&y);
ord[i][1]=2;
ord[i][2]=x;
ord[i][3]=y;
b[++lb]=y;
}
}
sort(b+1,b+1+lb);
for(int i=1; i<=lb; i++)
if(b[i]!=b[b[0]]) b[++b[0]]=b[i];
for(int i=1; i<=n; i++)
{
a[i]=search(a[i]);
treeinsert(1,b[0],a[i],i);
}
for(int i=1; i<=m; i++)
{
if(ord[i][1]==1)
printf("%d\n",b[query(1,b[0],ord[i][2]-1,ord[i][3],ord[i][4])]);
else
{
treedel(1,b[0],a[ord[i][2]],ord[i][2]);
a[ord[i][2]]=search(ord[i][3]);
treeinsert(1,b[0],a[ord[i][2]],ord[i][2]);
}
}
}
return 0;
}