树套树-区间第k大(带修改)
2016-01-05 16:44
225 查看
题目:zoj2112
题意:求区间第k大的数,有修改操作。
分析:这题可以树套树搞。
从网上看了很多博客才理解。。。资料1 资料2
我看的是树状数组套线段树版的。还有线段树套平衡树版的。
首先用一棵主席树(见这篇文章)维护没有操作前的数据。
然后用树状数组维护修改。
树状数组的每一个节点是一棵线段树,而且树状数组的每个节点都有一个管辖域(树状数组的性质没变)。
每更新一次,会修改log(n)个树状数组的节点。然而修改是不可取的,所以采用了新建了log(n)棵线段树的方法。
每新建一棵线段树,就是把原来版本的线段树先拷贝过来,然后新增一条路径(增加log(n)个节点)来代替那条要修改的路径。
代码(树状数组套线段树):
线段树套reap会超内存,我是动态分配内存的,删除时也delete了。。。最多n*log(n)个node,也就是用了5*50000*16个int(32000*1000 bit),加上另外开的两个数组200000个int(1600*1000 bit),总共32813kb,刚刚超了。。。
求解方法。。。
#include <bits/stdc++.h>
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->p<root->son[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<up)
{
mid=(down+up)>>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<maxn*3;i++) if(tree[i])
Destroy(tree[i]);
for(int i=1;i<=n;i++)
update(i,a[i],1,1,n,1);
char op[2];
while(m--)
{
scanf("%s",op);
int x,y,z;
if(op[0]=='C')
{
scanf("%d%d",&x,&y);
C(x,y);
}
else
{
scanf("%d%d%d",&x,&y,&z);
Q(x,y,z);
}
}
}
return 0;
}
题意:求区间第k大的数,有修改操作。
分析:这题可以树套树搞。
从网上看了很多博客才理解。。。资料1 资料2
我看的是树状数组套线段树版的。还有线段树套平衡树版的。
首先用一棵主席树(见这篇文章)维护没有操作前的数据。
然后用树状数组维护修改。
树状数组的每一个节点是一棵线段树,而且树状数组的每个节点都有一个管辖域(树状数组的性质没变)。
每更新一次,会修改log(n)个树状数组的节点。然而修改是不可取的,所以采用了新建了log(n)棵线段树的方法。
每新建一棵线段树,就是把原来版本的线段树先拷贝过来,然后新增一条路径(增加log(n)个节点)来代替那条要修改的路径。
代码(树状数组套线段树):
#include <bits/stdc++.h> 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<maxn;i+=(i&-i)) update(pos,v,1,num_cnt,BIT[i],BIT[i]); //在原来的版本上进行修改 } int buf1[20],buf2[20],buf_cnt1,buf_cnt2; int query(int k,int l,int r) { if(l==r) return r; int c=0; for(int i=0;i<buf_cnt2;i++) c+=Node[Node[buf2[i]].l].sum; for(int i=0;i<buf_cnt1;i++) c-=Node[Node[buf1[i]].l].sum; for(int i=0;i<buf_cnt1;i++) buf1[i]=(k<=c?Node[buf1[i]].l:Node[buf1[i]].r); for(int i=0;i<buf_cnt2;i++) buf2[i]=(k<=c?Node[buf2[i]].l:Node[buf2[i]].r); int m=(l+r)>>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 <bits/stdc++.h>
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->p<root->son[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<up)
{
mid=(down+up)>>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<maxn*3;i++) if(tree[i])
Destroy(tree[i]);
for(int i=1;i<=n;i++)
update(i,a[i],1,1,n,1);
char op[2];
while(m--)
{
scanf("%s",op);
int x,y,z;
if(op[0]=='C')
{
scanf("%d%d",&x,&y);
C(x,y);
}
else
{
scanf("%d%d%d",&x,&y,&z);
Q(x,y,z);
}
}
}
return 0;
}
相关文章推荐
- [BZOJ2006][NOI2010][RMQ/主席树][二叉堆]超级钢琴
- [BZOJ3196][TYVJ3196][树套数][区间第k大]二逼平衡树
- 主席树(可持久化线段树)入门专题
- zoj 2112 Dynamic Rankings 带修改区间第k大的几种解法
- hdu 5324 树套树、cdq分治
- BZOJ3209 花神的嘲讽计划I
- POJ 2104 K-th Number
- SPOJ COT 10628 Count on a tree
- ZOJ 2112 Dynamic Rankings
- HDU 4417 Super Mario
- HDU 4348 To the moon
- Codeforces 587C Duff in the Army
- bzoj-3123 森林
- poj 2104 K-th Number (主席树学习第一弹)
- hdu 4605 Magic Ball Game(主席树学习第二弹)
- hdu 4866 Shooting(主席树学习第三弹)
- hdu 3727 Jewel(主席树学习第四弹)
- SPOJ 10628 Count on The tree .. .. 主席树
- 主席树-区间第k大值(不带修改)
- 【题解】codeforces786C Till I Collapse