CF DS题做题笔记(持续更新)
作者:互联网
CF DS题做题笔记(持续更新)
题目来源:Codeforces 的 problemset内,difficulty 2300-2600,并包含“data structures”的 tag。
1681F
题意:给定一棵树,边有边权 \(w_i\),定义 \(f(u,v)\) 为,\(u,v\) 路径上只出现一次的权值个数,求 \(\sum_{1\le u < v \le n} f(u,v)\),\(1 \le n,w_i \le 5 \times 10^5\)。
分析:考虑求出每种边权对答案的贡献,将某条权值为 \(w\) 的边放到中间(图中标记的边权值为 \(w\),其余均不为 \(w\))。
将 \(<6,7>\) 放在中间。
考虑能使 \(<6,7>\) 产生贡献的路径个数。
显然,路径的一端要在 \(\{1,3,4,6\}\) 里面选择,另一端在 \(\{7,9\}\) 里面选择,可以理解为那些其他同权值的边把树断开了。
如果将所有权值为 \(w\) 的边删除,那么某一条权值为 \(w\) 的边对答案的贡献为两端连通块大小的乘积。
直接做时间复杂度为 \(O(n^2)\),考虑基于值域分治。
定义 \(solve(l,r)\) 为,求解所有 \(w_i \in [l,r]\) 的边的贡献和,我们可以先将 \(w_i \in [l,mid]\) 的所有边加入并查集,然后调用 \(solve(mid+1,r)\),结束后将 \(w_i \in [l,mid]\) 的所有边撤销,加入 \(w_i \in [mid+1,r]\) 的所有边,并调用 \(solve(l,mid)\),当 \(l=r\) 时,只有一种权值的边没有加入,在并查集上查询求解即可。
由于并查集要可撤销,故不能路径压缩,运用按秩合并保证复杂度,总复杂度为 \(O(n \log^2 n)\)。
此外这题可以用 LCT 求解,枚举到某一个权值,先将对应的边删除,求解,再加入,LCT 中维护树的大小即可,复杂度 \(O(n \log n)\)。
1625D
题意:给定 \(n\) 个整数,\(a_1,a_2,...,a_n\),并给定一个整数 \(k\),求出一个最大的集合 \(S\),满足 \(S\) 中的元素均 \(\le n\),且对于任意两个 \(S\) 中的元素 \(x,y\),满足两数异或和 \(\ge k\),\(1 \le n \le 3 \times 10^5,0 \le a_i,k \le 2^{30}-1\)。
分析:如果两个数异或结果的最高位比 \(k\) 大,则异或结果一定 \(>k\),定义 \(f(x)\) 表示当 \(x\) 不含有前导 \(0\) 的情况下二进制位数,考虑将所有数,在二进制表示中的末尾去掉 \(f(k)\) 位,并按新的数分组,若两个数处于不同组,则它们异或和一定 \(>k\)。
对每一组分别求解,容易发现每一组最多只能选上 \(2\) 个数,如果选 \(\ge 3\) 个数,就一定存在两个数,它们异或和 \(<k\),因为 \(k\) 的最高位上的数字一定会有 \(2\) 个 \(0\) 或 \(2\) 个 \(1\)。
设目前处理的那一组的数为 \(b_1,b_2,...,b_m\),即要求出是否存在两个数满足异或和 \(\ge k\),即求两个数最大异或和。将数转化为二进制,依次插入 trie 中,并查询最大异或和,即每次尽量往自己相反的方向走。
注意每一组求解完成后要清空 trie,不能 memset,要一个一个删除。
时间复杂度 \(O(n \log A)\)。
1575I
题意:给定一棵树,点有点权 \(a_i\),两点 \(x,y\) 之间边的长度定义为 \(\max\{|a_x+a_y|,|a_x-a_y|\}\),其中 \(|k|\) 表示 \(k\) 的绝对值,有两种操作,一种是修改某个点的点权,另一种是查询两点间简单路径的长度,\(1 \le n,q \le 10^5,0 \le |a_i|,|a_i^{'}| \le 10^9\)。
分析:考虑化简一下边长的表达式:
- \(a_x,a_y \ge 0\),显然边长为 \(a_x+a_y=|a_x|+|a_y|\);
- \(a_x,a_y < 0\) 显然边长为 \(-a_x-a_y=|a_x|+|a_y|\);
- \(a_x \ge 0,a_y < 0\),这个可以感性理解下,\(a_x-a_y\) 可以看做在数轴上将 \(a_x\) 右移 \(|a_y|\) 单位长度,\(a_x+a_y\) 可以看做在数轴上将 \(a_x\) 向左平移 \(|a_y|\) 单位长度,由于 \(a_x \ge 0\),所以边长为后者,即 \(a_x-a_y=|a_x|+|a_y|\);
- \(a_x<0,a_y \ge 0\),根据绝对值的性质,边长可以写做 \(\max\{|a_y+a_x|,|a_y-a_x|\}\),与情况 \(3\) 同理,可得边长为 \(|a_y-a_x|\),即 \(a_y-a_x=|a_x|+|a_y|\);
综上,\(x,y\) 之间的边长为 \(|a_x|+|a_y|\)。
设 \(x,y\) 之间简单路径上经过的点为 \(u_1,u_2,...,u_k\),其中 \(u_1=x,u_k=y\),则长度为 \(|a_x|+|a_y|+2\sum_{2}^{k-1}|a_{u_i}|\),可以通过树链剖分修改和求解。
注意在查询前判断 \(\text{lca}(u,v)\) 是否等于 \(u\) 或 \(v\),这决定了 \(u_2\) 或 \(u_{k-1}\) 的值。
时间复杂度 \(O(n \log^2 n)\)。
1528C
题意:给定两棵树,求一个最大的集合 \(S\),满足对于任意 \(x,y \in S\),在第一棵树中 \(x,y\) 一个是另一个的祖先,第二棵树中 \(x,y\)都不是对方的祖先。
分析:考虑从第一个条件出发,要想满足第一个条件,所有点一定是在树上的某一条深度不断递增的链上,一次 dfs 可以求出所有的链。
现在的问题是怎么满足第二条链,一个很显然的方法是,当选择一个点时,将第二棵树中它的祖先和子树内所有点打上标记,这个可以用树链剖分,但是很麻烦。
考虑将祖先关系对应到 dfs 序上来,定义 \(L_i,R_i\) 为节点 \(i\) 在第二棵树 dfs 序上的最左边出现位置和最右边出现位置,dfs 第一棵树的过程中,是否选择当前节点 \(u\),取决于是否在已经选择的区间中,有无包含 \([L_u,R_u]\) 的区间或被 \(L_u,R_u\) 包含的区间,分如下三种情况:
- 没有上述区间,则选择 \(u\)。
- 有被 \([L_u,R_u]\) 包含的区间,此时不选择 \(u\),这样不会使得答案变大,且会造成更多的非法节点。
- 有包含 \([L_u,R_u]\) 的区间,删除包含它的区间并加入 \([L_u,R_u]\),虽然不会使答案变大,但是会减小非法节点的数量。
注意回溯的时候要撤销标记。
判断区间的包含关系可以用线段树解决,总复杂度 \(O(n \log n)\)。
1508C
题意:给定 \(n\) 个点的完全图,有 \(m\) 条边 \(<u_i,v_i,w_i>\) 确定了边权,另外的边没确定边权。如果给没确定边权的所有边赋予一个非负权值,满足所有边权值的异或和为 \(0\),求赋权后图中最小生成树的最小权值。\(1 \le n \le 2 \times 10^5,1 \le m \le \min\{2 \times 10^5,\frac{n(n-1)}{2}-1\},1 \le w_i \le 2^{30}-1\)。
分析:很显然,赋权时,只需要将一条边赋成给定权值的异或和,其余赋 \(0\),考虑到如果 \(n\) 很大,相对来说 \(m\) 很小了,此时大多数都是没有标权值的边,一定会存在一个没有标权值的边构成的环,那么将所有没标权值的边赋 \(0\),此时不会影响答案。
当存在 \(n\) 条没标权值的边就一定存在环,,即 \(m > \frac{n(n-1)}{2}-n\),由于 \(m_{max}=2 \times 10^5\),所以当 \(n\) 大于一个大约 \(O(\sqrt{2 \times 10^5})\) 的数就一定存在环,其余情况下可以直接枚举。
现在的问题转换为如何求一个大多数边权均为 \(0\) 的完全图最小生成树,可以用类似于 BFS 的方法处理只考虑边权为 \(0\) 的边的连通关系,用 set 维护一下还没有考虑到的点即可。其余边用 Kruskal 往上加就行。
顺带提一句可以省去小数据枚举的方法,在跑 Kruskal 的时候记录哪些边被选上,跑完后重置 dsu,按边权从小到大排序,找到第一个连接两个不同连通块的边,且在第一遍 Kruskal 时没被选上,这样就可以选上它替代掉一条 \(0\) 边。
标签:10,le,复杂度,CF,异或,做题,权值,DS,边权 来源: https://www.cnblogs.com/znstz2018/p/16367473.html