CF DS题做题笔记(持续更新)

CF DS题做题笔记(持续更新)

题目来源:Codeforces 的 problemset内,difficulty 2300-2600,并包含“data structures”的 tag。

1681F

题意:给定一棵树,边有边权 wi,定义 f(u,v) 为,u,v 路径上只出现一次的权值个数,求 1u<vnf(u,v)1n,wi5×105


分析:考虑求出每种边权对答案的贡献,将某条权值为 w 的边放到中间(图中标记的边权值为 w,其余均不为 w)。

<6,7> 放在中间。

1687F P1

考虑能使 <6,7> 产生贡献的路径个数。

显然,路径的一端要在 {1,3,4,6} 里面选择,另一端在 {7,9} 里面选择,可以理解为那些其他同权值的边把树断开了。

如果将所有权值为 w 的边删除,那么某一条权值为 w 的边对答案的贡献为两端连通块大小的乘积。

直接做时间复杂度为 O(n2),考虑基于值域分治。

定义 solve(l,r) 为,求解所有 wi[l,r] 的边的贡献和,我们可以先将 wi[l,mid] 的所有边加入并查集,然后调用 solve(mid+1,r),结束后将 wi[l,mid] 的所有边撤销,加入 wi[mid+1,r] 的所有边,并调用 solve(l,mid),当 l=r 时,只有一种权值的边没有加入,在并查集上查询求解即可。

由于并查集要可撤销,故不能路径压缩,运用按秩合并保证复杂度,总复杂度为 O(nlog2n)

此外这题可以用 LCT 求解,枚举到某一个权值,先将对应的边删除,求解,再加入,LCT 中维护树的大小即可,复杂度 O(nlogn)


1625D

题意:给定 n 个整数,a1,a2,...,an,并给定一个整数 k,求出一个最大的集合 S,满足 S 中的元素均 n,且对于任意两个 S 中的元素 x,y,满足两数异或和 k1n3×105,0ai,k2301


分析:如果两个数异或结果的最高位比 k 大,则异或结果一定 >k,定义 f(x) 表示当 x 不含有前导 0 的情况下二进制位数,考虑将所有数,在二进制表示中的末尾去掉 f(k) 位,并按新的数分组,若两个数处于不同组,则它们异或和一定 >k

对每一组分别求解,容易发现每一组最多只能选上 2 个数,如果选 3 个数,就一定存在两个数,它们异或和 <k,因为 k 的最高位上的数字一定会有 2021

设目前处理的那一组的数为 b1,b2,...,bm,即要求出是否存在两个数满足异或和 k,即求两个数最大异或和。将数转化为二进制,依次插入 trie 中,并查询最大异或和,即每次尽量往自己相反的方向走。

注意每一组求解完成后要清空 trie,不能 memset,要一个一个删除。

时间复杂度 O(nlogA)


1575I

题意:给定一棵树,点有点权 ai,两点 x,y 之间边的长度定义为 max{|ax+ay|,|axay|},其中 |k| 表示 k 的绝对值,有两种操作,一种是修改某个点的点权,另一种是查询两点间简单路径的长度,1n,q105,0|ai|,|ai|109


分析:考虑化简一下边长的表达式:

  1. ax,ay0,显然边长为 ax+ay=|ax|+|ay|
  2. ax,ay<0 显然边长为 axay=|ax|+|ay|
  3. ax0,ay<0,这个可以感性理解下,axay 可以看做在数轴上将 ax 右移 |ay| 单位长度,ax+ay 可以看做在数轴上将 ax 向左平移 |ay| 单位长度,由于 ax0,所以边长为后者,即 axay=|ax|+|ay|
  4. ax<0,ay0,根据绝对值的性质,边长可以写做 max{|ay+ax|,|ayax|},与情况 3 同理,可得边长为 |ayax|,即 ayax=|ax|+|ay|

综上,x,y 之间的边长为 |ax|+|ay|

x,y 之间简单路径上经过的点为 u1,u2,...,uk,其中 u1=x,uk=y,则长度为 |ax|+|ay|+22k1|aui|,可以通过树链剖分修改和求解。

注意在查询前判断 lca(u,v) 是否等于 uv,这决定了 u2uk1 的值。

时间复杂度 O(nlog2n)


1528C

题意:给定两棵树,求一个最大的集合 S,满足对于任意 x,yS,在第一棵树中 x,y 一个是另一个的祖先,第二棵树中 x,y都不是对方的祖先。


分析:考虑从第一个条件出发,要想满足第一个条件,所有点一定是在树上的某一条深度不断递增的链上,一次 dfs 可以求出所有的链。

现在的问题是怎么满足第二条链,一个很显然的方法是,当选择一个点时,将第二棵树中它的祖先和子树内所有点打上标记,这个可以用树链剖分,但是很麻烦。

考虑将祖先关系对应到 dfs 序上来,定义 Li,Ri 为节点 i 在第二棵树 dfs 序上的最左边出现位置和最右边出现位置,dfs 第一棵树的过程中,是否选择当前节点 u,取决于是否在已经选择的区间中,有无包含 [Lu,Ru] 的区间或被 Lu,Ru 包含的区间,分如下三种情况:

  1. 没有上述区间,则选择 u
  2. 有被 [Lu,Ru] 包含的区间,此时不选择 u,这样不会使得答案变大,且会造成更多的非法节点。
  3. 有包含 [Lu,Ru] 的区间,删除包含它的区间并加入 [Lu,Ru],虽然不会使答案变大,但是会减小非法节点的数量。

注意回溯的时候要撤销标记。

判断区间的包含关系可以用线段树解决,总复杂度 O(nlogn)


1508C

题意:给定 n 个点的完全图,有 m 条边 <ui,vi,wi> 确定了边权,另外的边没确定边权。如果给没确定边权的所有边赋予一个非负权值,满足所有边权值的异或和为 0,求赋权后图中最小生成树的最小权值。1n2×105,1mmin{2×105,n(n1)21},1wi2301


分析:很显然,赋权时,只需要将一条边赋成给定权值的异或和,其余赋 0,考虑到如果 n 很大,相对来说 m 很小了,此时大多数都是没有标权值的边,一定会存在一个没有标权值的边构成的环,那么将所有没标权值的边赋 0,此时不会影响答案。

当存在 n 条没标权值的边就一定存在环,,即 m>n(n1)2n,由于 mmax=2×105,所以当 n 大于一个大约 O(2×105) 的数就一定存在环,其余情况下可以直接枚举。

现在的问题转换为如何求一个大多数边权均为 0 的完全图最小生成树,可以用类似于 BFS 的方法处理只考虑边权为 0 的边的连通关系,用 set 维护一下还没有考虑到的点即可。其余边用 Kruskal 往上加就行。

顺带提一句可以省去小数据枚举的方法,在跑 Kruskal 的时候记录哪些边被选上,跑完后重置 dsu,按边权从小到大排序,找到第一个连接两个不同连通块的边,且在第一遍 Kruskal 时没被选上,这样就可以选上它替代掉一条 0 边。

posted @   znstz  阅读(145)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示