Atcoder Beginner Contest 396 + 397 题解
Atcoder Beginner Contest 396 题解
D
题意
给你一张简单无向图,每条边带有权值,求从 1 到 \(N\) 的路径中所有权值异或最小的一条。
\(N\le 10\)
题解
首先异或路径绝对不能使用 Dijkstra 或 Floyd 算法,因为当前边的最小值不一定能拓展到别的顶点。
考虑到如此小的 \(N\) 范围,直接爆搜即可。
1 |
|
E
题意
给定三个数组 \(X,Y,Z\),求出一个数组 \(A\) ,满足 \(A_{X_i} \oplus A_{Y_i} = Z_i\),且 \(\sum_{i=1}^NA_i\) 最小或判断不存在。
题解
显然对于每个 \(i\) ,\(X_i\) 和 \(Y_i\) 有着一个约束关系,将它们之间连接一条边后会形成一个图,由于是异或操作,所以每一位可以独立考虑。
我们单独考虑每一位,不妨先不考虑真实存放的值 \(ans\),而是求出一个相对的异或值 \(v\) 使得约束关系成立,(这样处理,最后求出答案的时候再异或上一个 0 / 1即可)。
然后考虑如何求出满足条件的相对关系:我们使用并查集维护连接在一起的边,并在并查集中加入权值:设根节点的初始值为 0,每当一个数加入并查集时,如果本来就相连,那么判断是否冲突(两值异或后为边权),如果不相连,就合并,新节点的权值也应该更新。
考虑这样的并查集的实现,首先查找功能需要进行路径压缩,压缩过程中还需要往根节点异或路径顶点的权值,返回两个值:祖先节点和当前节点的相对权值;而合并功能由于存在祖先节点的更换,需要异或本身消除影响( \(a\oplus a = 0\))。
然后考虑答案的最小值:对于每一位,所有节点都是 0 或 1,那么如果 1 比较多,我们就给每个节点再异或上一个 1 ,使得 0 尽可能多,如果 0 比较多就不变。
1 |
|
F
题意
给定 \(n, m\) 和 长度为 \(n\) 的数组 \(a\),对于 \(k = 0,1,...,m-1\),使得 \(a\) 中每个数都加上 \(k\) 再对 \(m\) 取模。输出当前数组的逆序对数。
题解
显然一次操作后有影响的数就是原来的值为 \(m - k\) 的数,因为它会变成 0 ,使得与它前面位置的数形成更多逆序对,与后面位置形成逆序对的数量减少,而其他数的相对大小不变,逆序对数也不变。
我们设满足这种条件的数的下标为 \(v_1, v_2,...,v_x\) ,显然 \(v_i\) 会与前面的 \(v_i - i\) 个数都形成逆序对,之前与后面的 \(n - v_i - (x - i)\) 个数形成的逆序对会消除。
我们使用权值树状数组统计逆序对数后按照上面的方式处理即可。
1 |
|
Atcoder Beginner Contest 397 题解
D
题意
给定一个 \(N\),判断是否存在一对正整数 \(x, y\),满足 \(x^3 - y^3 = N\)。
\(N\le 10^{18}\)
题解
我们第一想法是枚举 \(y\) ,判断 \(N+y^3\) 能不能开三次方,这样做的复杂度是 \(O(\sqrt{N})\),很难通过。
考虑立方差公式:\(x^3 - y^3 = (x-y)\times(x^2+xy+y^2)\) ,我们令 \(d = x - y\) ,那么 \(x^2 + xy + y^2 \ge x^2 - 2xy+y^2 = d^2\),所以 \(d^3 \le N\),我们可以枚举 \(d\) ,令 \(N = (d + k) ^ 3 - k^3 = d^3 + 3d^2k + 3dk^2\) 一定会被 \(d\) 整除,接下来就是解方程 \(d^2 + 3dk + 3k^2 = \frac{N}{d}\),使用求根公式需要小心溢出,也可以使用二分求解。
时间复杂度: \(O(\sqrt[3]{N}\log N)\)
1 |
|
E
题意
给定一个有 \(NK\) 个节点的树,将这棵树分解为 \(N\) 条路径,每条路径上有 \(K\) 个节点,每个节点只能在一条路径中,判断是否可以分解。
题解
我们不妨从叶子节点贪心地往上拓展 \(K\) 个节点,那么从根节点 DFS 的过程中存在以下情况:
如果有三条及以上子链长度小于 \(K\) ,那么当前节点无论放在哪条链都不行。
如果恰好有两条子链长度小于 \(K\) ,那么必须满足两条链的节点数加上 1 等于 \(K\) ,也就是把它们与父节点合并在一起。
否则,当前节点放在子链中。
1 |
|
F
题意
在本场 C 题的基础上,把一个数组分解成三个非空数组,这三个数组的权值就是数组内不同元素的数量,求三个非空数组的权值和最大值。
题解
C 题的做法是求出原数组的前后缀答案,然后一次遍历求出最大值 \(\max(pre[i] + suf[i+1])\)。
然而本题可以分解为三个数组,也就是说如果原来的分割点为 \(i\) 的话,在 \([i+1, n)\)的区间里还可以再取一个分割点,直接模拟的话显然超时,我们不妨考虑各元素能产生的贡献:
设 \(nxt[i]\) 表示 \(i\) 位置右边第一个满足 \(a[j] = a[i]\) 的位置 \(j\) ,那么如果在 \([i, nxt[i] - 1]\) 之间进行分割的话,这个数就会额外产生 1 的额外贡献。
那么,我们自然的想法是寻找一个位置,它的额外贡献是最多的,也就是 区间更新 + 区间查询最大值,这样的操作我们使用一个懒标记线段树维护。
由于是额外贡献,我们只需要枚举一个区间分割点,然后在右边区间查找最大值即可(我们不关心最大值到底在哪里),同时还要记得消除贡献。
1 |
|