今天做了一下范浩强出的题目。
题目难度大致是2、3、1吧。
第二题:大融合
题目大意:给出一堆点,支持动态加边,保证加边后是一棵树,求任意时刻某一条边所在的联通块,有多少条简单路径经过这一条边。
显然这一条边被通过的次数,等于切掉这一条边后,两个联通块的点数乘积。实际上如果我们将这棵树变成有根树的话,就是较深点的子树大小,乘上总点数减去这颗子树大小。
设Size[i] 为以i为根的子树大小,可以用 LCT 维护这个值。(实际上合并两个联通块,值变化的只有接入点到根路径上的点的size值)
但是既然只会改变点到根路径上的size值,那么就可以考虑用更简单的版本,链剖就可以了。
更新时,就从接入点开始,直到当前这棵子树的根,全部加上另一棵子树大小这么大的值。快速地知道当前这一棵子树的根可以用“规定方向的并查集”。【一开始忘记了写路径压缩TLE了Orz】
这一道题很灵活地运用了树链剖分,算是一道好题吧。
第三题:路径
题目大意:给出一幅无向图,每个点上有一个符号或者数字,求走k步形成多少个合法表达式。
点数只有20个点,看起来是可以dp的。
设F[i][j][Len]表示起点为i,终点为j,走了k步,当前形成是一个没有前缀负号的表达式,对应的,g[i][j][Len]表示有前缀负号的表达式。
那么分三种情况讨论:1)在前面加一个负号,2)在左右加一对括号,3)用一个运算符把两个f值或g值并为一个
1、2还好处理,但是3处理起来会T。当时没时间想下去了,现在想了一下,应该这样是可以做的。
设g[i][j][Len]表示有前缀的+、×、/,h[i][j][Len]表示有前缀负号。那么3就转化成了两步:1)在一个f前面串上一个+、×或/号。 2)在一个有前缀符号的块前串上一个数。
这样子的话3的复杂度就降下来了。
还有另一种做法,设f[i][j][k][l]表示当前做到第i步,在第j个点上,左括号比右括号多k个,l表示最后一个数是否为单独的0。直接dp也是可以的,要注意的是考虑全面,列举一下可以和不可以的情况。
第一题:想法
题目大意:给出一堆集合,每次新建一个集合,由出现过的两个集合并成,问当前这个新集合有多少个元素。
神题!
正解是超神的概率算法。
首先给初始的集合一个[0, 1]区间内的权值,每一次合并两个集合,保留前T个数。
由于权值是随机的,假如这个新的集合包含k个数,那么第T个数期望是T/k。而我们保留的得到的第T个数是P的话,那么我们可以认为P=T/K,移项可以得到K=T/P。
然后重复做若干次,取平均值就可以了。
这样容许误差的题目很显然就是运用概率算法的,而利用随机数平均分布的性质可以有很大的帮助。
但是这样的概率算法也一定程度上依赖多次取平均。
这一次的比赛的教训就是拍得要全面吧。第二题我只拍了小数据,但是极限数据、特殊数据都没拍,结果全跪了,分别超时4个点和爆栈1个点。呵呵。
【拍。】