然而其实是上文化课的时候没法怎么碰电脑了..就把一些gym题打印了下来..然后写一写一些我没想出来的题的题解.
CF 100965C
http://www.cnblogs.com/tmzbot/p/5801251.html 其实感觉是个很正常的思路..我还要在套路上学习一个啊..
CF 100451I
给定一个递增数列,求这个数列是否存在某个排列是模n(给定)意义下的等差数列并输出通项.
我们考虑我们要找什么.要是我们能找到这个数列的公差那么我们这个问题就做完了.
我们考虑在数列中随机一个点(c),然后去掉这个数列中已这个点为中心对称的最长一段.这个其实很好做:如果数列中存在(x,y, c-xequiv y-c pmod{n}),那么我们就去掉这两个数. 我们来证明这个是对的: 显然可以除去所有的,然后对于一个数(x),如果(c-(x-c)mod n)不在数列中它肯定不会被除去. 那么我们就这样一步步(递归的)除掉这个序列, 让它每次除掉后结果数列的长度小于等于原长度*(一个设定的比例alpha), 那么就最多递归log层了. 复杂度是O(T(n)), T(n)是除一次的时间.如果ran一定次数都没法除成alpha我们就可以直接断言它不是等差数列了.
这样我们最后在数列长度很小的时候暴力求出那个长度, 最后对于原数列的每个数找一下它的prev就好了.
CF 100451H
维护一个对一个初始为空的双端队列(deque)的操作序列,初始时其中有1e9个空操作.在线,给出动作:
- [L] [op] [args] #[保证行L是一个空操作] 在行L插入操作op args, 格式后面详述
- [L] - #[保证行L非一个空操作] 删除行L内的操作.
- [L] ? [time] #查询从操作1运行到操作time最后双端队列front和back的值
[op] [args] 是一个deque操作,操作名为[op],有参数[args].具体的,
- push_front [value]
- pop_front
- push_back [value]
- pop_back
(保证每一个操作都是合法的)
对于每个动作,
- 如果为[L] [op]或[L] -,输出0 0(为了在线)
- 如果为[L] ?,输出[front] [back]
输出完记得fflush才能继续读入.
这道题目的思路其实非常经典,我们可以维护一个对操作序列的线段树,其中每一个节点维护这个deque两端的信息(加上哪些或者删去多少),这个信息可以用可持久的treap方便的维护(一点也不).(O(n log^2 n)).这个做法十分杭二机房(大雾),类似的题目见bzoj NOI十连鏼.
然而这个做法实在是太暴力(而且很难写)了.我们考虑,因为题目保证了数据的合法性,即当pop的时候deque一定非空,那我们就无需判断两个端点之间的关系了(反正都是合法的),那么我们就可以两个端点位置分开维护.
考虑左端点的做法,右端点同理
我们首先可以求出运行1-time最后的左端点, 这个很简单只需要区间和.
然后我们要求出这个位置最后的值,即:我们要找到最晚的在time+1之前的push操作使得这个操作加入的位置是给定端点处(无论是push_front或push_back).
这个有点难做的,不妨让我们想一想这个端点位置随时间变化的规律.显然相邻两个相差1((alpha)),但是靠近的两个push操作位置不一定相差1((eta)),但是如果不相差1的话一定是减少的((gamma)).
(alpha)启示了我们可以用二分(线段树上记相对位置min,max), 然而(eta)说不能直接二分(可能会炸), 然后看到(gamma)我们就需要分析一下.
首先我们想办法让查找不会递归到pop里去.这个简单, 设pop的min为intmax,max为intmin即可.(解决(alpha))
然后我们通过(gamma)的性质说明(alpha)的二分对(eta)的情况不会炸.
我们设查找的相对位置是L,递归到这样的一段: max>L, min<L 且不存在合法的push, 那么在这前面为了构造max和min的差,在上一段范围包括这个L(min<L, max>L)但是不存在L的下降序列之前必然有一段上升序列的序列,其中穿插的下降序列都不范围包括L(反证法),然后其中的一个上升序列包括L(从一个<L +1变化到 >L). 那么复杂度最多就是查找两次的复杂度(只会失败一次).
那么就做完了.(O(nlog n))
bzoj3068
想了好几天的一道题..Claris说的不是很明白然后他的代码又极其丑陋,= =..
其实有很多好想(难写)的做法..当然我都枪毙掉了..(比如什么区间加对数组逐个取max值的max还有什么线段树合并之类的..)最终其实还是要观察性质啊..
首先设根.然后我们考虑如何求出每棵子树的重心(带权)..
其实很简单啦..重心P一定具有性质,就是与P相连的每个联通块的权值和都小于等于总权值和的1/2.这个是经典的调整法证明..那么我们在一个节点Q上加一棵子树会让重心向P的方向移动.我们如何维护这个重心呢..?其实很简单啦..就是这棵树的重心一定在根节点或根节点的权值和最大子树中.(反证法,如果不在最重的中说明最重的子树<=1/2,那显然就是在根节点了),那么我们就可以让这个重心先继承最重子树的,然后再往上调整.复杂度的话..可以考虑移动过的节点都不会再被移动到了所以复杂度是对的..
现在的问题就是去掉一棵子树tr后的重心.这个问题还是有点点难的..一个比较自然的想法就是维护对每个节点p,维护max(wtot-wtr-wp,max(w(son(p)))),(...真的能维护么..看起来是可以的..)
然而其实并不用啊..我们先二分第一个w_anc>=1/2(wtot-wtr),那么重心要么是这个点要么在这个点除了n包含tr的子树外最大的子树里..而且一定是一个最小的wp>=1/2(wtot-wtr)..然后我们发现dfs序里dfn[儿子]>dfn[父亲]..然后似乎就直接线段树找一找就好了..