没能力去WC什么的只能来清北学堂了。
因为没什么空闲时间,所以就只能在上课做笔记贴在这了。
这两天刚开始觉得too easy,不过后来讲的题目挺有趣的,做法很巧妙。
其实还是做题太少了,太弱了。
=======================分割线========================
1.14并没有做笔记,因为较为简单,或者说忘记了。
随手写一点笔记吧。
今天上课内容还是数据结构。
线duang树还是有很多特性的东西没有了解到。
JSOI2008堵塞的交通:
十分玄学的维护内容,维护区间内的连通性/只用区间的边是否连通,然后转移。
但是维护完依旧十分毒瘤,因为询问也要分很多情况讨论。
JSOI2008 Blue Mary开公司
看上去要暴力计算几何,实际上不用。
线段树的一种用法——标记永久化。
每个区间记录下中点位置最高的线段,然后下放不知道谁优的线段。
显然中点最高的线段,相对于其他线段,都必然有一半或以上是更优的。
比较比较就行了。
BZOJ3938 Robot
和上一题差不多,每个机器人走的时间-位移曲线一定是一条折线
处理出所有折线,将折线拆成线段,然后就同上了。
还是很巧妙的。
关于标记永久化:
每个点的标记不用下放,而是改为记录多一个标记。
每次修改将标记留在那,返回时将其父亲区间的sum都加上这个数。
查询时一路往下走时,每经过一个区间相当于加了一个buff,
即经过这个区间的所有数都要加上这个区间的标记*区间len。
这是区间加/区间求和的简单用法。
然后就开始讲平衡树了。
treap:
每个点有key和rad两个值,key满足二叉排序树,rad满足堆。
插入:
我们先按照二叉排序树的方式,按key大小将新节点插入树——新节点一定是叶子。
然后赋予这个点一个随机的rad,再通过左旋右旋使得这个树是一个小(大)根堆。
如果新节点的rad<父亲节点的rad,旋转新节点。
删除:
将待删除节点的rad设为inf,旋转使他沉到叶子节点——看哪个儿子的rad更小就将小儿子旋转上来。
最后删除这个节点,最后更新路径上所有点的信息。
平衡树的标记——标记表示以该点为根的子树内地所有点都要统一应用一个操作。
在访问一个点时,保证它自身和左右儿子上都没有标记。
treap确定子树范围:
way1:给编号打标记——比如在3和4中间插入一个元素,原来的4~7变成5~8,再插入一个4。
way2:维护子树siz,推算当前区间端点。
——维护每个子树siz,维护除子树内的点以外,在当前点点前面有几个点。
入门例题:
BZOJ3224普通平衡树,虽然之前是splay做的…
NOI2004郁闷的出纳员
假装是例题
JSOI2008 火星人prefix:
对于任意字串hash[i,j]求法:
求出hash[i]=s[i]*e^0+s[i-1]*e^1+…
hash[j]=s[j]*e^0+s[j-1]*e^1+…
(hash[j]-hash[i-1]*e^(j-i+1)%mod+mod)%mod;
还要维护一下子树的hash………..
下午继续是数据结构啊。
没错就是splay0.0
NOI2005维修数列——其实挺裸的,插入一段数和删除一段都是一个个插/删就可以了。
全局最大子序列和就是普通的套路。
HNOI2011括号匹配——分析问题,发现不能匹配的括号形如)))(((,
若左括号长度为p,右括号长度为q,
那么我们修复这段的最小花费应该是p/2上取整+q/2上取整。
若左括号为1,右括号为-1
p=-最小前缀和,q=最大后缀和,同时我们发现p+q=总和,所以我们只用维护一个。
那么我们区间要记录什么?——mr最大后缀和,ml最大前缀和,sum区间和
——在区间翻转/取反后前两个是可以互相转换的。
ZJOI2012网格——一看就是毒瘤题啊
其实分析一下还是可以简化问题的。
首先我们发现这些同色边一定是一堆链,然后对于操作2,两个点之间某色路径唯一。
这样我们就可以拆成10副图,变色其实就是link-cut
维护这些链就是一段连续的链作一棵splay,
求两点间的max就是把区间转出来求,
断开就是x到根,再把x和右儿子断开成两个splay。
连接就是x和y转到根,y接到x的右儿子。
虽然我感觉没什么问题,但这满满的都是LCT的感觉。
接着讲了和上午比较线段的JSOI2008的题差不多的一个东西,将线段换成了抛物线。
http://codeforces.com/gym/100085这个的F题
大概问题在于怎么求两个壳的并,可以将每个壳的下顶点抠出来,然后看看每个点的包含关系。
接着是抛物线求交的计算几何什么的。
回过头看看BZOJ3224普通平衡树,是可以离线然后用线段树做的。
记下所有用到的x,离散化,建立权值线段树,就可以做了。
在要求了一波以后开始珂持久化线段树。
对于单点修改:
发现每次修改只会改logn个点区间,那么我们就每个点建立一个类似链表的东西,记下新的东西。
求区间第k大:
离散化,建立权值线段树,[l,r]区间保存权值落在[l,r]之间的数有多少个
普通的求区间和线段树,初始线段树全0
从左到右,加入序列中的每个数
每新加一个数就新增一棵线段树
从左到右第i棵树中的每个节点[l,r]保存的是:原序列前i个数中,权值落在[l,r]中的数有多少个
从左到右第i棵树中的每个节点[l,r]保存的是:原序列前i个数中,权值落在[l,r]中的数有多少个
如果同时查询i,j两棵树 (i<j) ( i < j )
可以知道前i个数中权值在[l,r]之内的数有几个
可以知道前j个数中权值在[l,r]之内的数有几个
相减
可以知道第i+1到第j个数中,权值在[l,r]之内的数有几个!
然后整体二分,令l=1,r=n,
求权值在[l,l+r>>1]以内的数有多少个,记为s
若s<=k,另r=l+r>>1,否则另l=(l+r>>1)+1,k-=s
转到第二步
BZOJ2588 Count on a tree
核心思想:通过记录前缀的线段树的有关信息,相减后得到答案。
那么序列上端点相减是可以得到一个区间的。
对于每个点家里一棵线段树,代表从根到他路径上所有点的权值构成的权值线段树。
这个东西是从它的父亲转移过来的。
u~v路径=u-lca+v-lca+lca单点,这就是三颗线段树一起查询的结果。
然后整体二分一下就行了。
树上求最长上升子序列也可以用珂持久化。
一道神奇的题目:
给出一个平面中的点集,问可以组成W的五元组有多少个。
可以看出一定是要枚举中间的这个点啦。
一种可行的思路是先求出一个大范围的答案减去错误的答案。
s(312)=s(312)+s(213)-s(213)
s(213)=X-s(123)
X=基准点左下角的点对点数量。
PS.其实有截图,但是不知道为什么保存在word里不见了。