主席树详解 zoj 2112 (单点更新)+ poj 2104 区间第k大
来源:互联网 发布:win8电脑屏幕录制软件 编辑:程序博客网 时间:2024/05/07 23:10
解决问题:单点更新,区间询问第k大的数是多少
缺点:容易MLE
复杂度:n*logn*logS (n个数,离散化后有S个不同的数)
1.树状数组套线段树:树状数组表示区间,理论上树状数组上每个元素对应一棵线段树,线段树rank区间在[l,r]的数已经有多少个,所有的线段树的结构都是一样的
2.离线操作,离散化,排序:离散化知道整个操作过程要有哪些数,排序后知道他们的rank是多少,rank值相当于一张表,当要在x位置改成u,在树状数组x,x+=x&(-x).....位置上更新线段树上rank[u]的位置,要查询[l,r]上第k小的数,得到此时第k小的数的rank,查rank表,知道这个数是多少
3.保持历史版本:由于每棵树的结构一样,所以可以共用节点。理论上有n棵线段树,但实际最开始为0,每次更新根据需要在原有的树上加入新节点
4.查询具有f[l,r]=f[1,r]-f[1,l-1]的性质
zoj 2112
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define MAXN 50050#define MAXM 10500class CMT{private: struct Node { int sz; Node *ch[2]; Node() { sz=0;ch[0]=ch[1]=NULL; } Node(int _sz,Node *ll,Node *rr) { sz=_sz;ch[0]=ll;ch[1]=rr; } int gets(){ return this?sz:0;} // 如果为空就返回0 int getls() { return this?ch[0]->gets():0;} int getrs() { return this?ch[1]->gets():0;} }pool[(MAXN+MAXM)*40],*a[MAXN],*b[MAXN],*c[40],*d[40]; // pool是总共节点数,a,b是n个数, int used,amount,left,right;//原来有n个数,避免一个个插入时n*logn*logS的复杂度,直接插入n*logS,在a数组上操作//之后单点更新在b数组上操作,n*logn*logS,b是树状数组模式的操作,b上每个元素对应一棵线段树的根 Node *malloc(Node *root,int w) { //if(pool[used]==NULL) //pool[used]=new Node(); //new出来后放在内存池,避免TLE Node *now=&pool[used++]; if(root==NULL)//b数组操作时 { now->sz=w; now->ch[0]=NULL;//每次复用时要初始化 now->ch[1]=NULL; } else//a数组操作时 { now->sz=root->sz+w; now->ch[0]=root->ch[0]; now->ch[1]=root->ch[1]; } return now; } Node *insert(Node *root,int x,int w,int l,int r)//a数组上的插入 { Node *now=malloc(root,w); if(l!=r) { if(x<=(l+r)/2) now->ch[0]=insert(now->ch[0],x,w,l,l+r>>1); else now->ch[1]=insert(now->ch[1],x,w,(l+r>>1)+1,r); } return now; } Node *change(Node *root,int x,int w,int l,int r)//b数组上的单点更新 { if(root==NULL) root=malloc(NULL,0); if(l!=r) { if(x<=(l+r>>1)) root->ch[0]=change(root->ch[0],x,w,l,l+r>>1); else root->ch[1]=change(root->ch[1],x,w,(l+r>>1)+1,r); } root->sz+=w; return root; } int query(int k,int lc,int ld,int l,int r) { if(l==r) return l; int sum=0; for(int i=0;i<lc;++i) if(c[i]) sum-=c[i]->getls(); for(int i=0;i<ld;++i) if(d[i]) sum+=d[i]->getls(); if(k<=sum) { for(int i=0;i<lc;++i) if(c[i]) c[i]=c[i]->ch[0]; for(int i=0;i<ld;++i) if(d[i]) d[i]=d[i]->ch[0]; return query(k,lc,ld,l,l+r>>1); } else // 如果k大于在左儿子上的个数,说明要找的数在右儿子上 { for(int i=0;i<lc;++i) if(c[i]) c[i]=c[i]->ch[1]; for(int i=0;i<ld;++i) if(d[i]) d[i]=d[i]->ch[1]; return query(k-sum,lc,ld,(l+r>>1)+1,r); } }public: void clear(int n,int l,int r)//l,r是整棵线段树的左右区间,根据全部数排序时是从0还是1开始排,决定l是0还是1 { used=0; amount=n; left=l; right=r; for(int i=0;i<=n;++i)// a,b都是1-n的 a[i]=b[i]=NULL; } void insert(int i,int x,int w) { a[i]=insert(a[i-1],x,w,left,right);//a[i]是1-i的叠加 } int query(int l,int r,int k)//要询问[l+1,r]的结果,即f(r)-f(l) { int lc=0,ld=0; c[lc++]=a[l]; d[ld++]=a[r]; for(int x=l;x;x-=x&(-x)) c[lc++]=b[x]; for(int x=r;x;x-=x&(-x)) d[ld++]=b[x]; return query(k,lc,ld,left,right); } void change(int i,int x,int w) { while(i<=amount) { b[i]=change(b[i],x,w,left,right); i+=i&(-i); } }}cmt;struct CMD{ int x,y,k; char ch;}cmd[MAXM];int a[MAXN],b[MAXM+MAXN],tot,n,m;int main (){ int T;scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); tot=0; for(int i=0;i<n;++i) { scanf("%d",&a[i]); b[tot++]=a[i]; } for(int i=0;i<m;++i) { scanf(" %c",&cmd[i].ch); if(cmd[i].ch=='Q') scanf("%d%d%d",&cmd[i].x,&cmd[i].y,&cmd[i].k); else { scanf("%d%d",&cmd[i].x,&cmd[i].k); b[tot++]=cmd[i].k; } } sort(b,b+tot); int cnt=1; for(int i=1;i<tot;++i) if(b[i]!=b[i-1]) b[cnt++]=b[i]; tot=cnt; cmt.clear(n,0,tot-1); for(int i=0;i<n;++i) { int j=lower_bound(b,b+tot,a[i])-b; cmt.insert(i+1,j,1);// i+1是a数组上更新的位置,j是对应的线段树更新的位置 } for(int i=0;i<m;++i) { if(cmd[i].ch=='Q') { printf("%d\n",b[cmt.query(cmd[i].x-1,cmd[i].y,cmd[i].k)]); //f[l,r]=f[1,r]-f[1,l-1] } else { int j=lower_bound(b,b+tot,a[cmd[i].x-1])-b; cmt.change(cmd[i].x,j,-1); a[cmd[i].x-1]=cmd[i].k; j=lower_bound(b,b+tot,a[cmd[i].x-1])-b; cmt.change(cmd[i].x,j,1); } } } return 0;}
poj2104
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define MAXN 105050#define MAXM 10050class CMT{private: struct Node { int sz; Node *ch[2]; int gets(){ return this?sz:0;} // 如果为空就返回0 int getls() { return this?ch[0]->gets():0;} int getrs() { return this?ch[1]->gets():0;} }pool[MAXN*50],*a[MAXN];// pool是总共节点数,a,b是n个数, int used,amount,left,right;//原来有n个数,避免一个个插入时n*logn*logS的复杂度,直接插入n*logS,在a数组上操作//之后单点更新在b数组上操作,n*logn*logS,b是树状数组模式的操作,b上每个元素对应一棵线段树的根 Node *malloc(Node *root,int w) { Node *now=&pool[used++]; if(root==NULL)//b数组操作时 { now->sz=w; now->ch[0]=NULL;//每次复用时要初始化 now->ch[1]=NULL; } else//a数组操作时 { now->sz=root->sz+w; now->ch[0]=root->ch[0]; now->ch[1]=root->ch[1]; } return now; } Node *insert(Node *root,int x,int w,int l,int r)//a数组上的插入 { Node *now=malloc(root,w); if(l!=r) { if(x<=(l+r)/2) now->ch[0]=insert(now->ch[0],x,w,l,l+r>>1); else now->ch[1]=insert(now->ch[1],x,w,(l+r>>1)+1,r); } return now; } int query(Node *lq,Node *rq,int k,int l,int r) { if(l==r) return l; int sum=rq->getls()-lq->getls(); if(k<=sum) { if(lq) lq=lq->ch[0]; if(rq) rq=rq->ch[0]; return query(lq,rq,k,l,l+r>>1); } else { if(lq) lq=lq->ch[1]; if(rq) rq=rq->ch[1]; return query(lq,rq,k-sum,(l+r>>1)+1,r); } }public: void clear(int n,int l,int r)//l,r是整棵线段树的左右区间,根据全部数排序时是从0还是1开始排,决定l是0还是1 { used=0; amount=n; left=l; right=r; for(int i=0;i<=n;++i)// a,b都是1-n的 a[i]=NULL; } void insert(int i,int x,int w) { a[i]=insert(a[i-1],x,w,left,right);//a[i]是1-i的叠加 } int query(int l,int r,int k) { return query(a[l],a[r],k,left,right); }}cmt;struct CMD{ int x,y,k;}cmd[MAXM];int a[MAXN],b[MAXN],tot,n,m;int main (){ scanf("%d%d",&n,&m); tot=0; for(int i=0;i<n;++i) { scanf("%d",&a[i]); b[++tot]=a[i]; } for(int i=0;i<m;++i) { scanf("%d%d%d",&cmd[i].x,&cmd[i].y,&cmd[i].k); } sort(b+1,b+1+tot); int cnt=1; for(int i=2;i<=tot;++i) if(b[i]!=b[i-1]) b[++cnt]=b[i]; tot=cnt; cmt.clear(n,1,tot); for(int i=0;i<n;++i) { int j=lower_bound(b+1,b+1+tot,a[i])-b; cmt.insert(i+1,j,1);// i+1是a数组上更新的位置,j是对应的线段树更新的位置 } for(int i=0;i<m;++i) { printf("%d\n",b[cmt.query(cmd[i].x-1,cmd[i].y,cmd[i].k)]); //f[l,r]=f[1,r]-f[1,l-1] } return 0;}
- 主席树详解 zoj 2112 (单点更新)+ poj 2104 区间第k大
- ZOJ 2112 & BZOJ 1901 Dynamic Rankings(主席树 单点更新 区间第K大)
- poj 2104 主席树(区间第k大)
- ZOJ 2112 Dynamic Rankings (主席树+单点修改,询问区间第K值)
- ZOJ 2112 Dynamic Rankings 树状数组套主席树 单点修改求动态区间第K大
- poj 2104 区间第k大 主席树
- 区间第k大(主席树)
- POJ 2104(K-th Number-区间第k大-主席树)
- POJ 2104(K-th Number-区间第k大-主席树)
- POJ 2104 K-th Number 主席树 静态区间第K大
- 【POJ 2104】(K-th Number-区间第k大-主席树)
- POJ 2104 K-th Number (主席树 静态区间第K大)
- poj 2104 K-th Number (静态区间第k大,主席树)
- [poj 2104 K-th Number] 主席树 区间第K大
- POJ 2104 K-th Number 静态区间第k大 主席树
- POJ 2104 K-th Number 主席树(求区间第k大)
- 【主席树 求区间第k大】poj 2104 K-th Number
- POJ-2104 K-th Number (主席树 不带修改区间第k大)
- OSASK笔记
- Java之哈希表
- Maya: 菜单 曲面 >挤出
- 关于字典树的一些常用知识
- 学生患血癌北大梦碎 一天自学6小时中大梦圆
- 主席树详解 zoj 2112 (单点更新)+ poj 2104 区间第k大
- HDU 1554 Palindromes(回文串+动态规划)
- 杭电1348-Wall
- UNIX网络编程——ioctl 函数的用法详解
- Maya: 菜单 曲面 >双轨成形>双轨成形1工具
- Maya: 菜单 曲面 >双轨成形>双轨成形2工具
- hdu(1862)EXCEL排序
- Transact-SQL数据库基本操作
- QQ群信息统计