资源简介 (共161张PPT)简单数据结构信息学奥林匹克竞赛知识点(难点)讲解——数据结构【尖端】1.序列维护(线段树&平衡树)线段树相信大家都会线段树了,所以就不讲原理了平衡树全称“平衡二叉搜索树”,常见的类型有:1.splay2.treap3.AVL Tree4.Red Black Tree5.Scape Goat Tree6.Weight Balanced Leafy Tree(特殊结构)二叉搜索树性质:一个节点x左子树所有点的关键字都比x的关键字小,右子树所有点的关键字都比x的关键字大平衡树限于篇幅,这里只讲一下treap和splaytreap“树堆”“Tree + Heap”性质:每个点随机分配一个权值,使treap同时满足堆性质和二叉搜索树性质复杂度:期望O( logn )treap设每个节点的关键字是key,随机权值是rand1.如果v是u的左儿子,则key[v] < key[u]2.如果v是u的右儿子,则key[v] > key[u]3.如果v是u的子节点,则rand[u] > rand[v]treapTreap维护权值的时候一般会把相同的权值放在同一个节点上所以一个treap节点需要维护以下信息:左右儿子关键字关键字出现次数堆随机值节点大小(即子树大小)说要讲模板,这里就利用一下hzwer的吧旋转平衡二叉搜索树主要通过旋转来保持树的平衡,即保证复杂度Treap的旋转旋转有单旋和双旋,treap只需要单旋,这一点比较简单Treap的插入先给这个节点分配一个随机的堆权值然后把这个节点按照bst的规则插入到一个叶子上:从根节点开始,逐个判断当前节点的值与插入值的大小关系。如果插入值小于当前节点值,则递归至左儿子;大于则递归至右儿子;然后通过旋转来调整,使得treap满足堆性质CodeTreap的删除和普通的BST删除一样:如果删除值小于当前节点值,则递归至左儿子;大于则递归至右儿子若当前节点数值的出现次数大于 1 ,则减一(通常将同一个权值缩掉)Treap的删除若当前节点数值的出现次数等于 1 :若当前节点没有左儿子与右儿子,则直接删除该节点(置 0);若当前节点没有左儿子或右儿子,则将左儿子或右儿子替代该节点;若当前节点有左儿子与右儿子,则不断旋转当前节点,并走到当前节点新的对应位置,直到没有左儿子或右儿子为止。CodeTreap的查询递归到叶子节点,一路维护信息即可Treap维护权值现在大家都会用treap来维护一个集合支持插入,删除,查询(kth,rank等)了吧Treap的其他功能Treap还可以支持维护序列时的分裂合并这里不详细讲了(我也不会)具体可以看luogu日报?splay“伸展树”“自适应查找树”splay每次对一个节点进行操作的时候通过一种方法把这个点旋转至根需要根据不同的情况判断应该怎么旋转,这里就不详细介绍了splaySplay具有“自适应性”大概就是说splay会根据操作的特点调整树结构,使得操作尽可能高效可以去了解了解splay的动态最优性猜想,是个著名的open problemDisadvantage可以通过势能分析证明splay的复杂度是均摊O( logn )的,也就是说splay在很多次操作中可能会有一次O( n )复杂度的操作而且这样的操作也很好构造所以splay不适合做一些需要撤销操作/可持久化的题目(虽然可以通过随机旋转什么的方法来规避,但还是感觉很吃力)自身常数比较大AdvantageSplay用来维护序列还是比较好写的,用来维护名次树感觉不好写由于自适应性,splay不需要特殊的技巧就可以高效启发式合并,还可以高效实现LCT(STT)等动态树打个广告——WBLT全称:Weight Balanced Leafy Tree这个Weight Balanced是指的Balanced by Boundary,也就是BB[α]和clj那个定义不一样大概可以理解为通过旋转而不是重构来满足替罪羊树那个平衡关系也就是说替罪羊树是Weight Balanced Tree的一种打个广告——WBLT线段树就是一种Leafy Tree,也就是说把信息都存在叶子上,非叶节点都是存储了信息的合并的虚点(大家可以感性理解一下大概是什么样的一个结构)优点:目前最好写的平衡树,可持久化效率很高缺点:非可持久化的情况下要两倍空间,拿来写LCT(STT)很吃力替罪羊树定义常数平衡因子α如果一个点的某个儿子,占到了子树大小的α,则认为不平衡,重构这个子树复杂度也是带均摊的,均摊O(logn),最坏单次操作O(n)复杂度证明平凡大概拿来解决什么样的题给你一个序列,每次查询区间的******给你一个树,每次查询链******Key是维护的可快速合并的信息,具体怎么定义快速合并比较复杂,这里不进行严谨介绍,只感性理解Fact其实这些题就改改线段树的merge函数之类的,相当无聊Luogu2023 [AHOI2009]维护序列1.区间加x2.区间乘x3.区间和取膜Problem如果只是区间加或者区间乘,直接打个标记就可以了但是同时有两个操作怎么办Solution当然是打两个标记啦,不过需要注意一下处理顺序维护两个标记,分别是加标记和乘标记分别设为add和mul如果一个节点的被加上了x则add+=x如果一个节点被乘上了x则add*=x,mul*=x注意取膜即对于标记按顺序维护先加后乘常见的打标记的操作区间加区间乘区间染色(区间修改为一个数)区间翻转区间xorLuogu4513 小白逛公园序列,单点修改,询问区间最大子段和Solution著名的新手杀手题。。。很经典来着对于每个区间,维护一个左边的最大前缀,右边的最大后缀,以及区间内部的答案每次合并的时候,即答案选取左子区间的max,右子区间的max,或者左子区间的最大后缀,右子区间的最大前缀即可很简单的题SolutionLuogu2042 [NOI2005]维护数列请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)Solutionpushdown就维护一下区间赋值和区间翻转的标记update就维护一下区间的最大前后缀和区间的最大子段和,然后更新就可以了Luogu5482 [JLOI2011]不等式组你需要维护一堆不等式1.插入一个ax+b>c的不等式2.删除第i个插入的3.查询x=k的时候成立的不等式个数Solution如果a>0:ax+b>c x > ( c – b ) / a如果a<0:ax+b>c x < ( c – b ) / a如果a=0:是否成立是确定性的开个平衡树维护值(按值域开个树状数组也行)然后每次插入取个整查询直接查rank即可注意细节Luogu1471 方差神犇HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数。他想算算这个数列的平均数和方差。操作1:1 x y k ,表示将第x到第y项每项加上k,k为一实数。操作2:2 x y ,表示求出第x到第y项这一子数列的平均数。操作3:3 x y ,表示求出第x到第y项这一子数列的方差。Solution可以通过维护区间和来维护区间平均数其实就是区间和/区间长度但是方差呢?Solution这里直接粘一个题解里面的公式了方差可以通过维护平方和和和的平方来算出来很多这种题直接推推式子就可以维护了比如SDOI那个无聊的东西某经典问题Solution假设x节点的儿子为y和zx相邻两数乘积的和为:y相邻两数乘积的和+z相邻两数乘积的和+y最右边的数*z最左边的数Solution假设x节点的儿子为y和zx任意两数乘积的和为:y任意两数乘积的和+ z任意两数乘积的和+y的和*z的和然后直接维护即可。。。Luogu4198 楼房重建有一排楼,每次把一个位置的楼的高度修改为x,每次输出可以从最左边看到的楼个数Solution1发现一个楼房能被看到可以等价于它的斜率比之前的任何一个都大所以说我们这里可以直接维护斜率,而不用管楼的高度问题转化为:1.单点修改2.查询全局有多少位置是前缀最大值Solution1可以试试分块维护复杂度好像是O( nsqrt( nlogn ) )的这里不仔细讲了Solution2考虑用线段树维护对于线段树每个结点维护两个值:ans和max,ans表示只考虑这个区间内的可以被看到的楼房,max表示这个区间的最大楼房斜率。如何合并区间?Solution2合并左右区间的时候:显然左区间的答案不会变化问题就是考虑右区间有多少个楼房在左区间的约束条件下仍然可以被看到Solution2如果右区间最大值都小于等于左区间最大值,那么右区间就没有贡献了,相当于是被整个挡住了。Solution2如果右区间最大值大于左区间最大值考虑右区间的两个子区间:左子区间、右子区间Solution2如果左子区间的最大值小于等于左区间最大值那么就递归处理右子区间因为相当于左子区间里面所有楼房都被前面的楼房挡住了了,递归查询右边有多少楼房没被挡住否则就递归处理左子区间,然后加上右子区间原本的答案,因为这个约束条件弱于左子区间对右子区间的约束,所以只考虑这个约束条件对左子区间的影响~Solution2由于要合并O( logn )次,每次合并会递归O( logn )个节点所以总复杂度O( mlog^2n )实际上常数非常小Luogu4036 [JSOI2008]火星人维护一个字符串序列1.单点插入2.查询两个区间的LCP的长度LCP就是两个字符串的最长公共前缀Solution如何判断两个字符串是否相等?哈希如何在带插入的情况下维护一个区间的哈希值?使用平衡树,预处理base的每个次幂的值,这样可以合并两个区间的哈希值如何查询LCP?Solution可以使用二分答案的方法二分一个区间长度,使用平衡树维护区间哈希的方法来查询这个长度的两个前缀是否相等时间复杂度O( mlog^2n ),可以优化为O( mlog^2n/loglogn )其实题目中说了询问次数比较少,询问是O( log^2n )的,插入是O( logn )的Luogu6327 区间加区间sin和Solution考虑这个区间sin和如何维护大家都记得数学课学过一个东西叫做和差角公式吗Solutionsin(x+y)=sinxcosy+cosxsinycos(x+y)=cosxcosy sinxsiny所以我们维护区间的sin和,cos和,然后就可以打区间加标记了,这个标记可以合并,也可以下放BZOJ4373: 算术天才⑨与等差数列(Luogu3792:由乃与大母神原型和偶像崇拜是这个的弱化版)给了你一个长度为n的序列1.询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。2.修改一个位置的值可以先思考一个简单版本:查询的k=1n<=5e5Solution1Luogu3792是BZOJ4373的弱化版,所以这里讲bzoj那个题的做法首先通过维护区间的min和max就可以知道这个区间是首项为多少,公差为k的等差序列了直接维护这个信息比较复杂,所以考虑有什么其他的方法可以快速维护Solution1首先维护区间的排序后的顺序非常困难所以考虑维护区间的某些信息,使得这个区间被随机打乱之后维护出来的值是一样的比如区间1 2 3 4 5和3 4 2 5 1会得到相同的结果Solution1首先可以想到区间和,但是比如1 3 5 7和1 4 4 7这两个的和是一样的,但是第一个是公差为2的等差序列,后面的不是Solution1那我们同时维护平方和,乘积,立方和之类的不就好了吗一个给定首项和公差的等差序列的和,平方和是很好计算的通过维护这些信息就可以极高概率确定这个区间是不是满足条件的了Solution1那如果出题人构造数据卡你呢有个确定性的做法,但感觉还是hash好玩其实这个题就是一个维护区间hash的思想Solution2有没有确定性算法呢?Solution2首先我们还是要维护区间的min和max,这样能知道首项和末项然后考虑一个等差数列的性质:公差为k的等差数列中任意选出两个元素,他们做差一定是k的倍数。这样想到,如果把原序列做个差分呢?Solution2把一个等差数列重排一下,然后做一个差分,这个差分数组的gcd一定是恰好等于k的,这个是必要条件。1.如果这个等差数列差分后gcd=a*k,我们发现这个等差数列的公差一定是a*k>k。2.如果这个等差数列公差是k,差分数组的gcd一定也是k,否则和1一样反证了。Solution2考虑把原序列差分,然后维护区间gcd。还需要什么?还需要区间中不能出现重复的数。这个我们对每个数维护前驱,然后变成一个数点的问题了。总时间复杂度O( mlog^2n )Solution2其实这个条件比数点弱,是查区间是否每个数的前驱都在区间外,所以维护区间前驱的max就可以了区间gcd是O( logn + logv )的总时间复杂度O( m(logn+logv) )Luogu3586 [POI2015]Logistyka维护一个长度为n的序列,一开始都是0,支持以下两种操作:U k a 将序列中第k个数修改为a。Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作。每次询问独立,即每次询问不会对序列进行修改。1<=n,m<=10000001<=k,c<=n,0<=a<=10^9,1<=s<=10^9。Solution对于每次询问:如果ai>=s,则ai可以每s次操作都被选中如果ai设数列中大于等于s的数有k个,小于s的数的和为sum。则只需要判断sum>=(c-k)*s即可Proof若sum<(c-k)*s则肯定不能,因为既然和都不够,所以根本不够取Proof若sum>=(c-k)*s由于每个数都小于s,所以有不少于c-k个数每次从最大的数开始取,一定存在解Solution发现需要维护小于x的数的个数,小于x的数的权值和于是我们用平衡树维护这个序列里面的所有值即可Luogu6105 [Ynoi2010]iepsmCmq给定一个常数 C,你需要维护一个集合 S,支持 n 次操作:1.插入x2.删除x每次操作结束后,需要输出从 S 集合中选出两个不同的元素,其的和 mod C 的最大值n<=5e5,强制在线,不过可以想想离线做法Solution我们把每个数对C取模,所以可以认为值域在[0,C)中发现x+y在[0,2C)中,所以最多减去一个C对每个x,找出最大的y使得x+y对每个x,找出最大的y(减去一个C)Solution发现x+y>=C的情况可以直接找出最大的两个x和y,平凡只需要考虑x+y如果我们把所有数都排序,假设x如果x1<=x2,则y1>=y2,这个满足单调性Solution1问题即,给出一个点集,支持插入删除,和查询max(x+y),使得x+y < C再继续分析一下,发现如果x,y < C/2,这个也是平凡的x,y < C/2可以推出 x+y < C,所以选两个最大的C/2以内的数就可以覆盖这部分的贡献Solution1目前非平凡的部分在于从[0,C/2)中选一个数x,[C/2,C)中选一个数y,x+y的贡献x+y我们可以认为最大化x+y是在最小化C-x-y于是用一棵平衡树维护,这里平衡树这个结构是用来满足x每个在[0,C/2)中的x就直接插入,每个在[C/2,C)中的y,就变成C-y然后插入平衡树需要维护子树内最小的C-y,最大的x,最小的C-y-xSolution1具体一点来说,所有[0,C/2)中的数x看做A集合,所有[C/2,C)中的数y变成C-y后看做B集合我们要在A和B集合中选出两个数a,b,使得:a平衡树可以维护这个Solution1这个信息显然可以合并于是我们使用分治结构,做到了O( logn )单次修改总时间复杂度O( nlogn )Solution2还可以发现:对每个A集合中的x,维护出其在B集合中的后继C-y对每个B集合中的C-y,维护出其在A集合中的前驱x这样一定是最优的,每次修改这个前驱后继变动是O(1)的这样就可以不用手写平衡树,只需要stl的set就可以了总时间复杂度O( nlogn )Luogu6617查找 Search序列,给定常数w1.单点修改2.查询区间是否存在两个数和为w5e5,4sSolution看到问题可以先想到二维数点的转化每个点x,设置其前驱为离其最近的w-x的位置这个和区间颜色数的转化类似如何带修改?Solution每次修改可能影响O(n)个位置:w-x x x x x x x…这样后面每个位置的前驱都是w-x如果修改了w-x的值,这样会导致O(n)个修改观察性质?Solution注意到这个是存在性判定如果存在两个(i1,j1),(i2,j2)使得a[i1]+a[j1]=w,a[i2]+a[j2]=w,而且[i2,j2]包含了[i1,j1],则(i2,j2)没有任何意义这样每个点只存在O(1)个配对关系Solution由于是存在性,所以我们维护b[i]表示每个点的前驱如果区间[l,r]内b[i]最大值在[l,r]中,则存在,否则不存在这样只需要rmq线段树,和set维护前驱后继即可O( n+mlogn )Luogu5069 [Ynoi2015]纵使日薄西山珂朵莉想让你维护一个长度为 n 的正整数序列 a[1],…a[n] ,支持修改序列中某个位置的值。每次修改后问对序列重复进行以下操作,需要进行几次操作才能使序列变为全 0(询问后序列和询问前相同,不会变为全 0):选出序列中最大值的出现位置,若有多个最大值则选位置标号最小的一个,设位置为 x,则将 a[x],a[x-1],a[x+1] 的值减 1,如果序列中存在小于0的数,则把对应的数改为 0。Solution可以发现如果一个位置被操作了,那这个值和旁边两个值会一起减,而且一个位置被操作意味着这个值不会比旁边两个值小所以这里旁边两个值就不会有贡献了,因为会被中间那个一直操作给提前减到0Solution将原序列进行极长单调划分发现对于每个极长单调区间,答案一定是所有奇数位置或者所有偶数位置的和Solution可以发现每次单点修改只会影响到这个点以及左右两个点是否成为局部极大值还有可能影响到旁边两个极长单调区间的状态这里影响是O(1)的,所以可以高效维护细节比较多O( mlogn )“李超线段树”Luogu4069 [SDOI2016]游戏Luogu4097 [HEOI2013]Segment区间对一个等差数列取max,查询单点的值,具体题意可以找那个题看看。Solution写个线段树每个节点维护一个永久化的标记,标记存的是一个等差数列,表示这个节点对这个等差数列取了maxSolution如果这个节点被打上了一个新标记Solution我们可以选择把一边的标记下放Solution那我们肯定考虑pushdown小的那一段可以发现每次pushdown的时候标记长度会/2每次pushdown的复杂度是O( logn ),每个标记会pushdown O( logn )次所以复杂度为O( mlog^2n )Solution查询就是,我们和线段树一样找出和这个位置有关的O( logn )个线段树节点,然后计算这个位置在这些节点上的高度,取个max。查询复杂度O( logn )应该可以通过多叉树平衡做到O( mlog^2n/loglogn )Luogu5608 [Ynoi2013]文化课n,m<=1e5,3sSolution对原序列建一棵线段树,考虑怎么在线段树上面修改和查询定义 极长 “X” 段为一个极长的子区间,使得区间中符号均为乘法区间值修改维护出区间中每个极长的 “X” 段的长度,可以发现这个存在一个自然根号:假设区间长度为size,则最多只有O( sqrt( size ) )种极长 “X” 段的长度每次对区间进行值修改的时候,即对这个长度为O( sqrt( size ) )的多项式进行求值,暴力计算即可,求值复杂度为O( sqrt( size ) ),即可以O( sqrt( size ) )的时间将一个节点值进行修改,同时维护信息区间符号修改每次对区间符号进行修改的时候,区间的信息只会变成区间和或者区间乘积,所以我们每个节点要维护区间和和区间乘积符号修改之后,这个节点的极长 “X” 段只会有O( 1 )种了符号进行修改可能影响一些节点的极长 “X” 段,考虑如何维护这个区间符号修改对一个节点,我们可以归并两个儿子的极长 “X” 段,来维护出这个节点的极长 “X” 段对一个大小为size的节点进行归并,代价是O( sqrt( size ) )的注意需要特判左儿子的最右极长 “X” 段是否和右儿子的最左极长 “X” 段进行合并区间信息合并合并区间信息的时候,只需要把左右儿子信息加起来,同时特判O(1)个位置即可这O(1)个位置就是左儿子的最右部分和右儿子的最左部分可以处理:标记对标记的影响标记对信息的影响信息和信息的合并所以我们正确性有保证了Complexity我们所有地方的复杂度都是O( sqrt( size ) )的线段树在每层只会递归到2=O(1)个节点中,所以每层只有O(1)个节点的信息需要更新ComplexityT( n ) = T( n / 2 ) + O( sqrtn )sqrt( n ) + sqrt( n / 2 ) + sqrt( n / 4 ) + … + sqrt( 1 ) = O( sqrtn )空间:T( n ) = 2T( n / 2 ) + O( sqrtn )sqrt( n ) + 2sqrt( n / 2 ) + 4sqrt( n / 4 ) + … + nsqrt( 1 ) = O( n )总时间复杂度O( msqrtn ),空间复杂度O( n )由于我不会多项式技术,所以不确定是否存在更优做法,但目前感觉不容易优一个poly(n)2.简单的均摊复杂度问题序列染色段数均摊特点:修改有区间染色操作用平衡树维护区间的颜色连续段区间染色每次最多只会增加O( 1 )个连续颜色段,用平衡树维护所有连续段即可均摊的颜色段插入删除次数O( n + m )序列染色段数均摊应用:区间染色,维护区间的复杂信息区间排序“ODT”类问题注意这里这个颜色段数均摊是有2~3的常数,常数很大随便YY的题1给一个序列,每个位置是一个3*3的矩阵1.区间修改为同一个矩阵2.查询区间矩阵从左到右的乘积要求1logSolution如果用线段树直接做的话,会发现复杂度是2log的我们线段树每次push_down的时候,要根据左右两个儿子的size而重新计算矩阵快速幂如何解决这个问题?Solution1我们可以把线段树建成一个2^k长度形式的然后记录下区间修改矩阵的2^0,2^1…2^k次幂的值Solution1每次push_down的时候,儿子的长度也是2^k形式的,这样可以直接利用记录的信息求解O( n+mlogn )Solution2使用序列上的颜色段均摊每个完整的颜色段我们计算出快速幂每次查询区间乘积时,发现会完整包含一些颜色段,以及边界上会有O(1)个不完全包含的段O(1)个不完整的段直接快速幂即可O( (n+m)logn )CF453E Little Pony and Lord Tirek给一个序列每个位置有初值ai,最大值mi,这个值每秒会增大ri,直到mi有m个发生时间依此增大的询问,每次询问区间和并且将区间的所有数字变成0Solution这个问题每次查询是区间查询并赋值,可以考虑颜色段均摊的方法假设一段上次修改时间是x,这次修改时间是y,这段的贡献怎么求?Solution将贡献分为这段时间充能满了和没满的分别讨论计算出一个数组a[i]表示i位置从0开始充能充a[i]秒,使得充a[i]+1秒后满Solution区间中没充满能的位置即所有i,满足a[i]区间中充满能的位置即上述的补,我们要求这些位置m[i]的和即变成区间a[i]有初值,颜色段均摊导致查询次数O(n+m)次总时间复杂度O((n+m)logn)“重量”平衡树平衡树旋转/重构的节点的size的和是O( nlogn )这样可以在旋转的时候暴力重构一些信息一般用来解决动态标号问题:序列1.在x后面插入y2.查询x和y在序列上的先后问题,这个要求O(1)可以线性解决,但是由于其他部分一般带log所以OI中一般采用O( nlogn )的解决方法“重量”平衡树应用:套用动态标号法可以得到平衡树维护后缀数组的算法,被称为“后缀平衡树”可以实现树套树的外层树插入Luogu5610 [Ynoi2013]大学序列1.区间x倍数/x2.区间和强制在线序列长度<=1e5,值域<=5e5Solution考虑到一个数最多被除log次,如果除数非1所以问题变成了如何快速找出x的倍数Solution我只会一个很无聊很幼稚很简单很暴力的做法就是把每个下标插入到其因数的所有平衡树里然后每次x的倍数/x,就在x对应的平衡树里面暴力查询一段区间的每个数是否是x倍数由于平衡树的复杂度是O( logn + s )(s是这个区间的点数)d(v)表示<=v的所有数里面最大的约数个数所以总复杂度是O( nd(v) + nlognlogv + mlogn )CF438D单点修改区间mod p区间和p <= 1e9Solution如果x>=2p,x mod p<=p<=2p/2<=x如果p<=x<2p,x mod p=x-p所以每个数每次会减半,最多logv次之后就变成0了线段树维护一个区间最大值,能减就减总复杂度O( (n+m)lognlogv )HDU 6315 Naive Operations给两个序列a和b,b是1-n的排列1.a区间加12.求区间内所有[ai/bi]的和Solution假设进行了n次全局加发现全局的和是sigma( n/1 + n/2 + … + n/n ) = O( nlogn )这是一个调和级数用树状数组维护答案序列于是每次如果有一个点的答案发生变化,就在一个点位置+1即可总复杂度O( mlog^2n )Solution怎么找出每次修改的位置呢线段树维护序列,每个位置初始是 -bi每次区间加1相当于线段树的区间加1每次操作完之后,找哪些位置是0,这个可以维护一个最大值来维护出来把这些0位置直接进行修改即可UOJ228. 基础数据结构练习题Solutionsqrt这个操作肯定是一个均摊,因为下降很快但是有区间加,怎么办呢想一想感觉可以维护值相同的连续段试试?Solution然后就TLE了发现会被奇怪的数据卡比如3 4 3 4 3 4开sqrt后变成1 2 1 2 1 2然后加2又变成3 4 3 4 3 4Oh no!Solution发现这种情况仅当a=b-1且b是完全平方数的时候会出现于是想办法维护一下区间极差就可以判掉这种情况由于取sqrt的次数是O( loglogv )的所以总复杂度是O( (n+m)lognloglogv )大概是使用所有连续段以外相邻位置的差来作为势能的均摊Luogu5068 [Ynoi2015]我回来了& [Code+#7]教科书般的亵渎珂朵莉在玩炉石传说的时候总是打不出教科书般的亵渎,于是他重新写了一个炉石传说’,并且将亵渎的描述改为:“等概率随机在 [L,R] 中选出一个整数作为伤害值 d,对所有随从造成 d 点伤害,如果有随从死亡,则再次施放该法术,但伤害值不重新随机;如果没有随从死亡,则停止释放”,还去掉了场面上随从上限和亵渎最多触发14次的限制。珂朵莉不知道这个改版亵渎的效果怎么样,于是他打算进行一些测试,其中共进行 m 次如下类型的操作:在场面上加入一个血量为 h 的随从,这里随从的血量都不能超过 n;给定 L, R,询问亵渎期望触发多少次;珂朵莉只会做操作1,于是他就把操作2交给你啦。保证: n<=1e5,h<=n,m<=1e6;Solution我们考虑亵渎什么时候可以被触发首先他一定会触发一次,那么这个时候需要让场上所有人的血量-d,想要触发下一次,一定需要有一个人的血量在[1,d]的范围内,如果触发第三次呢?不难发现就是需要有一个人的血量在[d+1,2d]的范围内,以此类推所以对于伤害值为d的情况,亵渎能够被触发的次数就是最大的k,使得对任意i在[1,k),存在 hj∈[i*(d-1)+1,i*d)因为每次只有插入一个数,所以我们发现对每个d,k一定会逐渐变大Solution变化量是多少呢?可以发现这个实际上是n/1+n/2+…+n/n=O( nlogn ),是一个调和级数我们用平衡树维护每个d当前延伸到的位置,每次插入x的时候即在平衡树上暴力找出有哪些d需要继续向后延伸,然后一直延伸每次一个d改变了延伸到的位置后需要一个树状数组进行单点修改总时间复杂度O( nlog^2n + mlogn )[Ynoi2014]誰も彼もが、正義の名のもとにSolution考虑用平衡树维护序列,将一段连续的同色极长段缩为一个点发现操作具有对称性,本质上只有区间染色为0/1区间每个数or/and上左/右区间和这三种操作Solution发现第二种操作其实就是让区间中0/1的同色极长段各自向左/右扩散一格的位置我们修改的时候不修改这个给定的区间,而是让区间两端点根据操作种类和所在的同色极长段的颜色进行调整,具体来说就是检查一下是否包含这个同色极长段,然后调节我们这次修改的区间的位置然后问题便转化为,每次区间中0/1的同色极长段各自变大1或者缩小1,这个可以通过在缩点平衡树结构上打标记来实现Solution因为有可能一个同色极长段在缩了之后变成0,所以我们需要维护一下区间内最小的同色极长段长度,如果是0则暴力将其删去,同时合并两边的段可以发现每次修改只会增加O(1)个同色极长段,每次删除可以花O(logn) 的代价减少一个同色极长段总时间复杂度O((n+m)logn) ,空间复杂度O(n)Luogu3747 [六省联考2017]相逢是问候Solution由欧拉定理,每个数最多变换O( logv )次因为这里指数每次变为phi(p)当p为偶数时,phi(p)至少/2当p为奇数时,phi(p)为偶数如果phi(p)=1,则继续下去值不变Solution于是暴力即可需要维护区间中每个数是否下次还能进行题目中的操作每个数可以操作O( logv )次,每次操作需要O( logv )的快速幂,这里均摊的代价是O( log^2v )总复杂度O( (n+m)(logn+logv)logv )Old Driver Tree在有类区间染色操作,以及保证数据随机的情况下可以用一个平衡树维护颜色连续段的暴力来解决复杂度期望O( (n+m)loglogn ),可以做到O( n + m ),但是not practicalOld Driver Tree在线可以做到O( (n+m)loglogn )(直接做),O( (n+m)logloglogn )(使用exponential tree),O( n + m )(使用O( logn/logw )的动态前驱数据结构)离线可以简单的做到O( n + m ),压位即可Old Driver Tree可以证明复杂度是期望O( (n+m)logn )的假设有k种操作,其中一种是区间染色,定义势能为颜色段个数每次操作的时候有两种可能性:1.以O( x )的代价消除O( x )势能,势能+2,概率1/k2.以O( x )的代价啥都没做,势能+2,概率1-1/k势能的均摊是O( n + m )于是这部分总的代价是O( (n+m)k )所以复杂度瓶颈在于平衡树而不是暴力的均摊部分…Old Driver Tree应用:各种题都能用,基本上不会被卡因为大部分出题人都觉得序列维护的那种数据结构题只需要随机数据就足够强了…不过要注意,别被没有区间染色的部分分给卡了…可以水掉各种题EffectCF1446D2给一个序列求最长的子段使得其中有至少两个出现次数最多的元素。输出最长子段长度。Solution可以证明,这两个出现次数最多的元素中,必定有一个是全局的众数Solution假设众数x出现次数为a,我们目前考虑一个值y,计算y与x的答案最大是多少,y出现b次我们如果得到一个O(a+b)的算法,这个题就是根号题了我们要得到一个O(b*polylog(n))的算法Solution初始将每个x出现的位置标记为无意义的位置我们枚举y出现的每个位置,然后找离这些位置最近的x出现的无意义位置(左右两边都找),然后将这些位置标记为有意义的位置Solution可以证明标记结束后无意义的位置和答案无关一个位置A无意义,即对其前面所有y出现的位置B,[B,A]之间一定x出现次数>y,同理对后面也成立,所以这个位置不可能是答案端点所以只有O(b)个可能的答案端点,这里用线段树维护就是O(blogn)的Solution由于所有数的出现次数和为n所以得到一个O(nlogn)的算法CF765F Souvenirs区间查两个数的差的最小绝对值Solution考虑i位置和哪些位置j能形成有意义的二元组有意义的二元组即对答案有影响的(i,j)如果是aiSolution考虑aiak1.ai>ak则|aj-ak|>|ai-ak|,这里有|ai-ak|<1/2|ai-aj|,出现了值域减半2.ai则|ai-aj|>|ai-ak|,这里限制了一个下界,之后再出现ai总的有贡献的二元组为O(nlogv)个扫描线+线段树,总时间复杂度O(nlognlogv+mlogn)Thanks for listening 展开更多...... 收起↑ 资源预览