BZOJ 1901 Dynamic Rankings
摘抄自cydiater大神
可持久化似乎是近几年才开始在OI界流传起来的,很难找到很靠谱的博客。
这篇文章写的很不错。我这里也参考了他的代码写法,写一篇小总结防呆。
算法主体
可持久化,即可以在原有的基础上返回历史版本,类似于一些软件的Ctrl+Z。
主席树,即可持久化线段树,或者说是函数式线段树,即线段树的可持久化。
如何实现可持久化?最简单的,每次有一个操作就把整棵树复制一遍,再在新树上操作。但是显然这个是存在大量优化的空间的。
就像上面这个图,这是一颗[1,8]的线段树(几何画板画的不要介意,大概知道什么意思就好了),
如果修改了编号为4的元素,那么红色的部分显然是要修改的,但是黑色的部分并不需要修改。于是每次修改只要在原树上新增O(logN)个节点就行了。这样就做到了即更新了下一版本的信息又不更改上一版本的信息。
是不是觉得有什么不妥?在新版本中怎么实现对黑色部分的访问?
我们之所以产生这样的疑问,是因为在朴素的线段树中,root节点的左儿子和右儿子必定是root×2和root×2−1,即满足二叉树的性质,而在主席树中如果按照上面那样说就很容易理解成转为多叉实现。
其实如果换成类似于平衡树的方式实现就比较好理解了。在每次更新红色部分的时候我们只需要更新左儿子或右儿子其中一个,对于另一个直接指向上一个版本就好了。
不带修改的区间第K大
一个很经典的问题。给定一段序列,要求一个数据结构,支持两个操作。1.修改某个数。2,查询某段区间的第K大。
首先考虑简单点的情况,只支持操作2。
如果只是查最大,就是线段树模板,而第K大就可以套上主席树。
这个长度为N的序列分为N个线段树,第i个线段树存着[1,i]的信息。很显然这里存的信息和普通的序列不一样。
第i个线段树存储着区间[1,i]的每个数出现的次数。数值过大?离散化即可。
为什么这样处理?首先考虑给定区间[L,R],要求第K大的数字,首先我们把第L−1棵线段树和第R棵线段树取出来,做差。就可以得到在区间[L,R]内每个数出现的次数。
这样子的话提前在每个节点上搞一个sum,就可以用平衡树找rank的方式实现了。
但是如果光按照上面所说建N棵线段树肯定不可行,空间上是O(N2)。用上面所说的可持久化优化即可。
即相当于给定一段区间,每次在区间的某一段上+1,然后用历史版本之类的。
带修改的区间第K大
带修改的第K大,或者说动态第K大。
相对来说比较麻烦。首先思考,在求静态第K大时,我们把[L,R]的线段树用TR于TL−1差分得到。如果把每次操作看作一个值,那么这就是一个前缀和的思想。
而维护前缀和的相关信息最优越的数据结构就是BIT(树状数组)了。
所以先用静态树状数组那一套把所有版本的线段树搞出来。然后再开一颗主席树,用BIT来维护修改。
因为权值线段树是支持加减的,所以把所有的操作和原始版本的做和,就是所求的线段树了,总体复杂度是O(log2N×N)。
以上就是介绍,下面放题
1901: Zju2112 Dynamic Rankings
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 8389 Solved: 3479
[Submit][Status][Discuss]
Description
Input
Output
对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
Sample Input
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
6
HINT
Source
用以上的知识就可以解决了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #include <bits/stdc++.h> #define ll long long using namespace std; inline int read(){ int x=0; int f=1; char ch= getchar (); while (! isdigit (ch)) { if (ch== '-' ) f=-1;ch= getchar ();} while ( isdigit (ch)) {x=x*10+ch- '0' ;ch= getchar ();} return x*f; } const int MAXN=5e5+10; struct Chair_Man_Tree{ int son[2],sum; }T[MAXN]; struct node{ int a,b,k; char op; }Q[MAXN]; int arr[MAXN],fsort[MAXN],root[MAXN],rsum=0,sum=0,n,m,cnt=0,top[2],prevL[MAXN],prevR[MAXN]; namespace zhangenming{ inline int lowbit( int x) { return x&-x;} void init(){ n=read();m=read(); sum=n; for ( int i=1;i<=n;i++){ fsort[i]=arr[i]=read(); } for ( int i=1;i<=m;i++){ char op[5]; scanf ( "%s" ,op); Q[i].op=op[0];Q[i].a=read();Q[i].b=read(); if (op[0]== 'Q' ) Q[i].k=read(); else fsort[++sum]=Q[i].b; } sort(fsort+1,fsort+sum+1); rsum=unique(fsort+1,fsort+sum+1)-fsort-1; } inline int Newnode( int sum, int son0, int son1){ T[++cnt].sum=sum;T[cnt].son[0]=son0; T[cnt].son[1]=son1; return cnt; } inline void build( int &root, int last, int pos, int leftt, int rightt){ root=Newnode(T[last].sum+1,T[last].son[0],T[last].son[1]); if (leftt==rightt) return ; int mid=(rightt+leftt)>>1; if (pos<=mid) build(T[root].son[0],T[last].son[0],pos,leftt,mid); else build(T[root].son[1],T[last].son[1],pos,mid+1,rightt); } inline int Get( int leftt, int rightt, int rnk){ if (leftt==rightt) return leftt; int summ=0; for ( int i=1;i<=top[0];i++) summ-=T[T[prevL[i]].son[0]].sum; for ( int i=1;i<=top[1];i++) summ+=T[T[prevR[i]].son[0]].sum; int mid=(leftt+rightt)>>1; if (rnk<=summ){ for ( int i=1;i<=top[0];i++) prevL[i]=T[prevL[i]].son[0]; for ( int i=1;i<=top[1];i++) prevR[i]=T[prevR[i]].son[0]; return Get(leftt,mid,rnk); } else { for ( int i=1;i<=top[0];i++) prevL[i]=T[prevL[i]].son[1]; for ( int i=1;i<=top[1];i++) prevR[i]=T[prevR[i]].son[1]; return Get(mid+1,rightt,rnk-summ); } } int k=0; inline void insert( int leftt, int rightt, int &root, int pos, int val){ if (!root) root=Newnode(0,0,0); T[root].sum+=val; if (leftt==rightt) return ; int mid=(rightt+leftt)>>1; if (pos<=mid) insert(leftt,mid,T[root].son[0],pos,val); else insert(mid+1,rightt,T[root].son[1],pos,val); } void solve(){ for ( int i=1;i<=n;i++){ arr[i]=lower_bound(fsort+1,fsort+rsum+1,arr[i])-fsort; build(root[i+n],root[i+n-1],arr[i],1,rsum); } for ( int i=1;i<=m;i++){ if (Q[i].op== 'Q' ){ top[0]=top[1]=0; prevL[++top[0]]=root[((Q[i].a-1)==0?0:Q[i].a+n-1)]; prevR[++top[1]]=root[Q[i].b+n]; for ( int j=Q[i].a-1;j>=1;j-=lowbit(j)){ prevL[++top[0]]=root[j]; } for ( int j=Q[i].b;j>=1;j-=lowbit(j)){ prevR[++top[1]]=root[j]; } printf ( "%d\n" ,fsort[Get(1,rsum,Q[i].k)]); } else { for ( int j=Q[i].a;j<=n;j+=lowbit(j)){ insert(1,rsum,root[j],arr[Q[i].a],-1); } arr[Q[i].a]=lower_bound(fsort+1,fsort+1+rsum,Q[i].b)-fsort; for ( int j=Q[i].a;j<=n;j+=lowbit(j)){ insert(1,rsum,root[j],arr[Q[i].a],1); } } } } } int main(){ //freopen("All.in","r",stdin); //freopen("bai.out","w",stdout); using namespace zhangenming; init(); solve(); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)