题目:zoj2112
题意:求区间第k大的数,有修改操作。
分析:这题可以树套树搞。
从网上看了很多博客才理解。。。资料1 资料2
我看的是树状数组套线段树版的。还有线段树套平衡树版的。
首先用一棵主席树(见这篇文章)维护没有操作前的数据。
然后用树状数组维护修改。
树状数组的每一个节点是一棵线段树,而且树状数组的每个节点都有一个管辖域(树状数组的性质没变)。
每更新一次,会修改log(n)个树状数组的节点。然而修改是不可取的,所以采用了新建了log(n)棵线段树的方法。
每新建一棵线段树,就是把原来版本的线段树先拷贝过来,然后新增一条路径(增加log(n)个节点)来代替那条要修改的路径。
代码(树状数组套线段树):
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e5+6;
const int LOG = 20;
struct node
{
int l,r;
int sum;
}Node[maxn*LOG];
int root[maxn],node_cnt; //静态主席树,所用节点数
int BIT[maxn]; //树状数组
int numbers[maxn],num_cnt; //离散化之后
int a[maxn],n,q;
struct command
{
char op;
int L,R,K;
}com[10006];
void build(int l,int r,int &rt)
{
rt=++node_cnt;
Node[rt].sum=0;
if(l==r) return ;
int m=l+r>>1;
build(l,m,Node[rt].l);
build(m+1,r,Node[rt].r);
}
void update(int pos,int v,int l,int r,int &rt,int pre)
{
rt=++node_cnt;
Node[rt]=Node[pre];
Node[rt].sum+=v;
if(l==r) return ;
int m=(l+r)>>1;
if(pos<=m)
update(pos,v,l,m,Node[rt].l,Node[pre].l);
else
update(pos,v,m+1,r,Node[rt].r,Node[pre].r);
}
void modify(int pos,int v,int rt)
{
for(int i=rt;i>1;
if(k<=c)
return query(k,l,m);
else
return query(k-c,m+1,r);
}
inline int Find(int x)
{
return lower_bound(numbers+1,numbers+1+num_cnt,x)-numbers;
}
void Q(int L,int R,int K)
{
buf_cnt1=0;
buf1[buf_cnt1++]=root[L-1];
for(int i=L-1;i>0;i-=(i&-i))
buf1[buf_cnt1++]=BIT[i];
buf_cnt2=0;
buf2[buf_cnt2++]=root[R];
for(int i=R;i>0;i-=(i&-i))
buf2[buf_cnt2++]=BIT[i];
int q=query(K,1,num_cnt);
printf("%d\n",numbers[q]);
}
void C(int pos,int v)
{
modify(Find(a[pos]),-1,pos);
modify(Find(v),1,pos);
a[pos]=v;
}
int main()
{
int nCase;
scanf("%d",&nCase);
while(nCase--)
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
numbers[i]=a[i];
}
num_cnt=n;
char ch[2];
for(int i=1;i<=q;i++)
{
scanf("%s",ch);
if(ch[0]=='Q')
{
scanf("%d%d%d",&com[i].L,&com[i].R,&com[i].K);
com[i].op='Q';
}
else
{
scanf("%d%d",&com[i].L,&com[i].R);
com[i].op='C';
numbers[++num_cnt]=com[i].R;
}
}
sort(numbers+1,numbers+num_cnt+1);
num_cnt=unique(numbers+1,numbers+num_cnt+1)-numbers-1;
root[0]=node_cnt=0;
build(1,num_cnt,root[0]);
for(int i=1;i<=n;i++)
{
int f=Find(a[i]);
update(f,1,1,num_cnt,root[i],root[i-1]);
}
for(int i=1;i<=n;i++) //初始化树状数组,最初没有修改信息
BIT[i]=root[0];
for(int i=1;i<=q;i++)
'Q'==com[i].op?Q(com[i].L,com[i].R,com[i].K):C(com[i].L,com[i].R);
}
return 0;
}
线段树套reap会超内存,我是动态分配内存的,删除时也delete了。。。最多n*log(n)个node,也就是用了5*50000*16个int(32000*1000 bit),加上另外开的两个数组200000个int(1600*1000 bit),总共32813kb,刚刚超了。。。
求解方法。。。
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
struct node //定义Treap节点
{
node* son[2]; //左右子树
int v; //值
int p; //优先级
int sz; //以当前节点为根节点的树的大小
node(int x)
{
son[0]=son[1]=NULL;
v=x;
p=rand();
sz=1;
}
void pushup() //增加或删除节点需要修改子树的sz
{
sz=1;
if(son[0]) sz+=son[0]->sz;
if(son[1]) sz+=son[1]->sz;
}
};
inline int cmp(int a,int b)
{
if(a==b) return -1;
return a>b?0:1;
}
inline void Rotate(node* &root,int d) //d为0时左旋(右节点向上更新), d为1时右旋(左节点向上更新)
{
node* k = root->son[d^1];
root->son[d^1] = k->son[d] ;
k->son[d] = root;
root->pushup();
k->pushup();
root = k ;
}
void Insert(node* &root,int x)
{
if(NULL==root)
root = new node(x);
else
{
int d = root->v>x?0:1; //这样写的话支持相同元素插入
Insert(root->son[d],x);
if(root->pson[d]->p)
Rotate(root,d^1);
}
if(root)
root->pushup();
}
void Erase(node* &root,int x)
{
int d = cmp(root->v,x);
if(-1==d)
{
node* u = root;
if(!root->son[0])
{
root = root->son[1];
delete u;
}
else if(!root->son[1])
{
root = root->son[0];
delete u;
}
else
{
int d2 = (root->son[0]->p>root->son[1]->p?1:0);
Rotate(root,d2);
Erase(root->son[d2],x);
}
}
else
Erase(root->son[d],x);
if(root)
root->pushup();
}
void Destroy(node* &root)
{
if(root->son[0])
Destroy(root->son[0]);
if(root->son[1])
Destroy(root->son[1]);
delete root;
root=NULL;
}
int Find(node* root,int x)
{
if(NULL==root)
return 0;
int ret=0,lnum=0;
if(root->son[0])
lnum+=root->son[0]->sz;
if(x>=root->v)
return lnum+1+Find(root->son[1],x);
else
return Find(root->son[0],x);
}
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 50006;
node* tree[151000];
int a[maxn],n,m;
void update(int pos,int v,int d,int l,int r,int rt)
{
d==1?Insert(tree[rt],v):Erase(tree[rt],v);
if(l==r) return ;
int m=(l+r)>>1;
pos<=m?update(pos,v,d,lson):update(pos,v,d,rson);
}
int query(int L,int R,int x,int l,int r,int rt)
{
if(L<=l && r<=R)
return Find(tree[rt],x);
int m=(l+r)>>1,ret(0);
if(L<=m)
ret+=query(L,R,x,lson);
if(R>m)
ret+=query(L,R,x,rson);
return ret;
}
int work(node* root,int x)
{
int ret=INF;
while(root)
{
if(root->v>=x)
{
if(ret>root->v)
ret=root->v;
root=root->son[0];
}
else
root=root->son[1];
}
return ret;
}
int q(int L,int R,int x,int l,int r,int rt)
{
if(L<=l && r<=R)
return work(tree[rt],x);
int m=(l+r)>>1,ret=INF;
if(L<=m)
ret=min(ret,q(L,R,x,lson));
if(R>m)
ret=min(ret,q(L,R,x,rson));
return ret;
}
void Q(int L,int R,int K)
{
int down=-INF,up=INF,mid,ret;
while(down>1;
// printf("%d\n",mid);
int c=query(L,R,mid,1,n,1);
if(c>=K)
up=mid;
else
down=mid+1;
}
printf("%d\n",q(L,R,down,1,n,1));
}
void C(int pos,int v)
{
update(pos,a[pos],0,1,n,1);
update(pos,v,1,1,n,1);
a[pos]=v;
}
int main()
{
int nCase;
scanf("%d",&nCase);
while(nCase--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=0;i