题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112
大意
给定一个长为
N
N
N的数组,有
M
M
M次操作,第i次操作要么是询问区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]中的第
k
k
k小的数,要么是将第
q
i
q_i
qi个数改为
c
i
c_i
ci。
思路
这是支持动态修改的区间第k大(小)问题,用树状数组套主席树的方式来解决问题。
主席树的思路是前缀和,故对一个区间的查询操作是
O
(
1
)
O(1)
O(1)级别的的(忽略查询的时间复杂度),但若要支持修改则修改一棵主席树上的值需要修改其后所有主席树的值,修改操作是
O
(
N
)
O(N)
O(N)级别的(忽略插入的时间复杂度),而树状数组套主席树则是在求和及插入的时候均用树状数组求和及插入的思想,将求和与插入操作的时间复杂度都降到
O
(
l
o
g
N
)
O(logN)
O(logN)(忽略查询的时间复杂度)。
但是这里有一个小问题:如果在最开始插入所有初始值的时候就用树状数组的方式插入,那么建完初始树的空间复杂度为
O
(
N
l
o
g
O(Nlog
O(Nlog2
N
)
N)
N),总的空间复杂度就变成了
O
(
N
l
o
g
O(Nlog
O(Nlog2
N
+
M
l
o
g
N
)
N+MlogN)
N+MlogN),很不巧ZOJ这道题只给了32M的空间。
数组开大了MLE开小了段错误,不大不小TLE(结合在HDOJ做题的经验,应该还是访问越界了)
参考了大佬学长的代码(https://blog.csdn.net/xiji333/article/details/97388973)后发现可以考虑一开始用普通主席树的方式插入(即前缀和),对于修改部分再用树状数组的思路处理,这样总的空间复杂度是
O
(
N
l
o
g
N
+
M
l
o
g
N
)
O(NlogN+MlogN)
O(NlogN+MlogN),考虑到
N
<
=
50000
N<=50000
N<=50000,这算是个可怕的优化了。
这是炸空间的代码
#include<stdio.h>
#include<map>
#include<queue>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#define MOD 10000000007
#define N 50005
#define Q 10005
#include<stdlib.h>
#include<string.h>
typedef long long LL;
using namespace std;
typedef struct node{
int lson,rson,va;//va:权值 主席树
}node;
typedef struct ope{
int a,b,c;
}ope;
int lowbit(int x){return x&(-x);}
int tot,n,m,te,len,s,t,k,que_,q1,q2;
int a[N],T[N+Q];
node tree[N*55];
char mov[10];
ope op[Q];
int R[N+Q];
int s1[60],s2[60];
void init()
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);T[i]=a[i];
}
for(int i=1;i<=m;i++)
{
scanf("%s",mov);
scanf("%d%d",&op[i].a,&op[i].b);
if(mov[0]=='C')
{
T[n+(++que_)]=op[i].b;op[i].c=0;
}else scanf("%d",&op[i].c);
}
sort(T+1,T+n+que_+1);
len=unique(T+1,T+n+que_+1)-T-1;
}
int find(int target)
{
return lower_bound(T+1,T+len+1,target)-T;
}
void insert(int pre,int &root,int l,int r,int pos,int val)
{
tree[++tot]=tree[pre];root=tot;tree[root].va+=val;
if(l==r)
{
return;
}
int mid=(l+r)/2;
if(pos>mid) insert(tree[root].rson,tree[root].rson,mid+1,r,pos,val);else insert(tree[root].lson,tree[root].lson,l,mid,pos,val);
return;
}
int query(int k,int l,int r)
{
if(l==r)return l;
int sum=0,mid=(l+r)/2;
for(int i=1;i<=q1;i++)sum-=tree[tree[s1[i]].lson].va;
for(int i=1;i<=q2;i++)sum+=tree[tree[s2[i]].lson].va;
if(sum>=k)
{
for(int i=1;i<=q1;i++)s1[i]=tree[s1[i]].lson;
for(int i=1;i<=q2;i++)s2[i]=tree[s2[i]].lson;
return query(k,l,mid);
}else
{
for(int i=1;i<=q1;i++)s1[i]=tree[s1[i]].rson;
for(int i=1;i<=q2;i++)s2[i]=tree[s2[i]].rson;
return query(k-sum,mid+1,r);
}
}
void add(int pos,int val)
{
int v=find(a[pos]);
for(int i=pos;i<=len;i+=lowbit(i))
{
insert(R[i],R[i],1,len,v,val);
}
return;
}
int main()
{
scanf("%d",&te);
while(te--)
{
memset(R,0,sizeof(R));
memset(tree,0,sizeof(tree));
scanf("%d%d",&n,&m);que_=0;tot=0;
init();
for(int i=1;i<=n;i++) add(i,1);//初始化时就用树状数组的方式插入
for(int i=1;i<=m;i++)
{
if(op[i].c)
{
q1=q2=0;
for(int j=op[i].a-1;j;j-=lowbit(j))s1[++q1]=R[j];
for(int j=op[i].b;j;j-=lowbit(j))s2[++q2]=R[j];
printf("%d\n",T[query(op[i].c,1,len)]);
}else
{
add(op[i].a,-1);a[op[i].a]=op[i].b;add(op[i].a,1);
}
}
}
return 0;
}
这是AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e4+5;
const int M=2400005;
struct node
{
int lc,rc,siz;
}tree[M];//节点
struct qury
{
int a,b,c;
}q[10010]; //离线操作
char op[10];//指令
int n,m,ansx,ansy,tot,len;//n个数 m个查询 tot个节点 len为离散化后的长度
//ansx ansy 用于记录 查询操作 所需要的节点个数
int source[maxn],rt[maxn],a[maxn],b[maxn],qx[maxn],qy[maxn];//记录静态主席树根节点编号 新增节点编号 原序列 离散化后的序列
// qx qy 用于记录 查询操作 所需要的节点
inline int lowbit(int x)
{
return x&(-x);
}
inline void update(int &root,int pre,int l,int r,int val,int num)
{
root=++tot;
tree[root]=tree[pre];
tree[root].siz+=num;
if(l==r)
return;
int mid=(l+r)>>1;
if(val<=mid)
update(tree[root].lc,tree[pre].lc,l,mid,val,num);
else
update(tree[root].rc,tree[pre].rc,mid+1,r,val,num);
}
inline void add(int pos,int v)
{
int val=lower_bound(b+1,b+len+1,a[pos])-b;
for(int i=pos;i<=n;i+=lowbit(i))
update(rt[i],rt[i],1,len,val,v);
}
inline int query(int x,int y,int l,int r,int k)
{
if(l==r) return l;
int mid=(l+r)>>1;
int sum=tree[tree[y].lc].siz-tree[tree[x].lc].siz;
for(int i=1;i<=ansx;++i)
sum-=tree[tree[qx[i]].lc].siz;
for(int i=1;i<=ansy;++i)
sum+=tree[tree[qy[i]].lc].siz;
if(k<=sum){
for(int i=1;i<=ansx;++i)
qx[i]=tree[qx[i]].lc;
for(int i=1;i<=ansy;++i)
qy[i]=tree[qy[i]].lc;
return query(tree[x].lc,tree[y].lc,l,mid,k);
}
else{
for(int i=1;i<=ansx;++i)
qx[i]=tree[qx[i]].rc;
for(int i=1;i<=ansy;++i)
qy[i]=tree[qy[i]].rc;
return query(tree[x].rc,tree[y].rc,mid+1,r,k-sum);
}
}
int build(int l,int r)
{
int root=++tot;
if(l==r)
return root;
int mid=(l+r)>>1;
tree[root].lc=build(l,mid);
tree[root].rc=build(mid+1,r);
return root;
}
inline void prework()//初始化操作 包括离散化等
{
tot=len=rt[0]=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
b[++len]=a[i];
}
for(int i=1;i<=m;++i)
{
scanf("%s",op);
scanf("%d %d",&q[i].a,&q[i].b);
if(op[0]=='Q')
scanf("%d",&q[i].c);
else if(op[0]=='C')
q[i].c=-1,b[++len]=q[i].b;
}
sort(b+1,b+1+len);
len=unique(b+1,b+1+len)-b-1;
// source[0]=build(1,len);
for(int i=1;i<=n;++i)
{
int val=lower_bound(b+1,b+len+1,a[i])-b;
update(source[i],source[i-1],1,len,val,1);
}
for(int i=1;i<=n;i++)
rt[i]=source[0];
}
inline void mainwork()//查询操作
{
for(int i=1;i<=m;++i)
{
if(q[i].c>=0)
{
ansx=0,ansy=0;
for(int j=q[i].b;j;j-=lowbit(j))
qy[++ansy]=rt[j];
for(int j=q[i].a-1;j;j-=lowbit(j))
qx[++ansx]=rt[j];
printf("%d\n",b[query(source[q[i].a-1],source[q[i].b],1,len,q[i].c)]);
}
else
{
add(q[i].a,-1);
a[q[i].a]=q[i].b;
add(q[i].a,1);
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--)
{
prework();
mainwork();
}
return 0;
}