CSP2019 题解

CSP2019 题解

D1T1 格雷码(code)

题目传送门

https://loj.ac/problem/3208

题解

按照题意模拟就可以了。

对于第 \(i\) 位,如果 \(k \geq 2^i\) 那么这一位就是 \(1\),然后把 \(k\) 变成 \(2^{i + 1} - k - 1\)。否则这一位为 \(0\),\(k\) 不变。

代码

https://loj.ac/submission/687508


D1T2 括号树(brackets)

题目传送门

https://loj.ac/problem/3209

题解

考虑在每一个点 \(i\) 处求出从根到 \(i\) 的括号序列中的所有后缀的合法数量 \(f_i\),其余的子串的贡献可以直接从父亲处得到。

维护一个栈存储从根到 \(i\) 的没有被匹配的左括号的位置 \(x\),那么显然如果 \(i\) 上是右括号,那么 \(f_i = f_x + 1\),否则 \(f_i = 0\)。

记得在每一个点的处理中记录被弹掉了哪个点,方便回溯。

代码

https://loj.ac/submission/687509


D1T3 树上的数(tree)

题目传送门

https://loj.ac/problem/3210

题解

作为从根本上把 CSP 难度推向弱胜省选难度的题目,这道题需要分成好几块来讲。

菊花图

菊花的部分分很简单(我又没想出来)。

很容易发现把某一个连接根的边断掉以后,这个点的点权会和根交换,于是这个点的点权就变成了上一个被断的边的点权。

于是可以发现所有的点之间存在这样的关系:

\[
\text{根} \to v_1 \to v_2 \to \cdots \to v_{n - 1} \to \text{根}
\]

也就是说,点权的移动形成了环的关系。我们只需要构造出这个环。

维护很多个链,从小到大枚举每一个点权,找到对应的点,它应该连向目前是链底的一个点中编号最小的点。注意只有在一条链的点数已经满了的时候才可以形成环。

这个可以用并查集或者把链缩成"链底 \(\to\) 链顶"的格式来维护。

这个部分分的代码:https://loj.ac/submission/687489 的 Task2。

在链上,如果想要把 \(x\) 上的点权移动到 \(y\),那么需要满足如下条件:

  • 那么对于 \(x\):两个邻边中朝向 \(y\) 的边必须是第一个断掉的。
  • 对于中间的点:两个邻边中朝向 \(x\) 的边必须要比 \(y\) 先断。
  • 对于 \(y\):两个邻边中朝向 \(x\) 的边必须是最后一个断掉的。

于是我们只需要维护每个点的两个邻边的断边顺序即可。从小到大枚举点权,从对应的点上来一次 \(dfs\),可以一边扫一遍判断合不合法,找到最小的可以移动到的点就可以了。

这个部分分的代码:https://loj.ac/submission/687489 的 Task3。

Full

可以发现,对于一个点来说,和菊花类似,以它为根的整棵树中,每一个子树中的点权的去往顺序也依然是形成了一个链的关系:
\[
\text{根} \to v_1 \text{的子树} \to v_2 \text{的子树} \to \cdots \to v_{n - 1} \text{的子树} \to \text{根}
\]
这个链的关系既是点权的移动顺序,也可以表示删边顺序,就是先删 \(v_1\),再删 \(v_2\)……

对于一条路径 \(x \to y\),要把 \(x\) 上的点权移动到 \(y\),那么和链类似,我们可以做出如下的限制:

  • 对于 \(x\):邻边中朝向 \(y\) 的边必须是第一个断的;
  • 对于中间的点:领边中朝向 \(x\) 的点必须恰好在 \(y\) 前一个断。
  • 对于 \(y\):领边中朝向 \(x\) 的边必须是最后一个断的。

我们考虑如何用上面的链的关系来表达断边顺序:

  • \(e\) 是第一个断的:\(rt \to e\);
  • \(e\) 是最后一个断的:\(e\to rt\);
  • \(e_1\) 恰好比 \(e_2\) 前一个断:\(e_1 \to e_2\)。

于是我们就可以对于每个点维护一堆链表来操作,和菊花一样,同样是只有在链满了的时候才可以形成环。

代码

https://loj.ac/submission/687959


D2T1 Emiya 家今天的饭(meal)

这道题没做出来是我一辈子的耻辱。

题目传送门

https://loj.ac/problem/3211

题解

看上去就很像容斥对吧。

如果我们不考虑“每种主要食材至多在一半的菜(即 \(\lfloor \frac k2 \rfloor\) 道菜)中被使用”的限制的话,那么这个题目显然就是每种烹饪方法的可以做出来的菜的数量 \(+1\) 的乘积再 \(-1\)。

想要如果解决这个限制可以考虑容斥。一半这个特殊的限制非常优美,它使得容斥只需要进行一层就可以了(因为不存在有两个食材同时超过一半)。

于是我们枚举哪一个食材超过了一半,假设这种食材为 \(A\)。那么我们维护这样的东西 \(dp[i][j][k]\) 表示前 \(i\) 个烹饪方式中,其中 \(j\) 个烹饪方式用了食材 \(A\),\(k\) 个没有使用的方案数。\(dp\) 的时候直接类似 \(01\) 背包转移就可以了。

最终的限制条件就是 \(j > k\)。

但是这样做是 \(O(mn^3)\) 的。可以获得 \(88pts\)。

因为我们只需要保证 \(j > k\) 即 \(j - k > 0\),所以我们可以直接维护 \(j - k\) 而不是分开维护 \(j\) 和 \(k\)。

时间复杂度 \(O(mn^2)\)。

代码

https://loj.ac/submission/687509


D2T2

题目传送门

https://loj.ac/problem/3212

题解

并不会证明,只能抽象理解。

可以发现每一个数的贡献就是要乘上它的所在段的和。所以我们要尽量最小化每一段的和,也就是尽量最小化最后一段的和(因为和是递增的)。

然而这个理解方式非常不严谨。

也许可以这样考虑:

把一个子段划分为两段,如果存在两种可以行的划分方式:前面一段的和为 \(s_1\),后面一段的和为 \(s_2\),中间夹着一个数 \(a\),我们考虑应该把 \(a\) 放进前面还是后面。其中 \(s_1 + a \leq s_2\)。

于是
\[
(s_1 + a)^2 + s_2 ^ 2 = s_1^ 2 +s_2 ^ 2 +2as_1 + a^2\s_1^2 + (a + s_2)^2 = s_1 ^ 2 + s_2 ^ 2 + 2as_2+a^2
\]
因为 \(s_1 < s_2\) 所以显然选择前者更优,也就是让后面的那一段小一些更优。

然而这样考虑还是很不严谨,算了自闭了不证了。

这样我们的目标就很明确的:最小化最后一段的和。

令 \(f_i\) 表示前缀 \(1..i\) 的最后一段的和的最小值。转移的时候我们需要保证 \(f_j \leq s_i - s_j\) 其中 \(s_i\) 表示前缀和。

\(f_j \leq s_i - s_j\) 等价于 \(f_j + s_j \leq s_i\)。当满足这个条件时,应该尽量取 \(j\) 大的。

所以如果对于 \(j, k\) 满足 \(f_j + s_j > f_k + s_k, j < k\) 那么 \(j\) 就可以被舍弃了。

同时因为 \(s_i\) 是递增的,所以我们可以维护一个单调栈,其中只保留最后一个满足要求的 \(j\),这个 \(j\) 就是 \(i\) 的决策点。

最后因为答案爆炸了 ll,所以需要开一个 \(128\) 个字节的高精度(使用两个 \(ll\) 实现)。

但是如果直接开的话会把 \(1G\) 的空间限制开炸了,所以我们维护前面的每一个点的决策点,最后只需要用一个 \(128\) 字节的变量统计答案就可以了。

代码

我比较懒,不想写高精度,所以用了 int128(其实高精度也很好写)。

https://loj.ac/submission/687514


D2T3 树的重心(centroid)

题目传送门

https://loj.ac/problem/3213

题解

考场上想到一个非常繁琐的做法,不敢写。

因为两个联通块并的重心应该在原来的两个块各自的重心之间的链上,所以可以直接在链上二分或者倍增。

但是这样还需要特判一对东西,比如连通块不包含哪个子树啊之类的。很麻烦。

所以考场还是乖乖地写了 \(75pts\)。

后来因为我的 \(75pts\) 算法最后的二叉树内容本身就非常古怪,所以想到了一个和一个可行的正解有不小的交集的做法。

我的二叉树做法大概就是发现一个点的重心只能在这个点为整棵树的根以后的最重的儿子中,(显然)所以在完全二叉树上可以直接暴力跳(每个点深度不超过 \(\log n\))。

后来发现了一个可行的正解也用了这个思路。

既然在最重的儿子的子树里面,那么实际上对于一棵树,重心只能在那条从根连下去的重链里面。

于是我们考虑动态维护每一个点为根的重链,然后在上面倍增跳就可以了。

维护重链的时候,每次选择包括其父亲在内的点中最重的儿子作为重儿子。然后因为我们需要避开一个子树,所以顺便维护一下次重的儿子,在需要避开重儿子时使用。

代码实现中需要先预处理每一个子树的重链的倍增数组,然后换根的时候再维护一个倍增数组(这个倍增数组就可以把父亲作为重儿子)。然后在重链上倍增找到最后一个子树大超过一半的点。

(感觉我讲得语无伦次

代码

https://loj.ac/submission/687516

原文地址:https://www.cnblogs.com/hankeke/p/csp2019_sol.html

时间: 2024-10-05 05:04:43

CSP2019 题解的相关文章

CSP2019题解

按照CSP题目顺序来写 格雷码 不难发现答案可以递归找到,然后每一次做即可. 代码 括号树 简单题,直接在树上搞一个栈然后回溯即可. 括号树nmsl 代码 树上的数 咕咕咕 Emiya 家今天的饭 很显然可以看出这题可以容斥,然后就可以写一个\(O(mn^3)\)的\(dp\). 然后考虑后面那两维状态可以做一个差,这样子就优化成了\(O(2mn^2)\),然后就可以过了. 代码 划分 \(\texttt{__int128}\)不香吗 代码 树的重心 代码 原文地址:https://www.cn

csp2019 Emiya家今天的饭题解

qwq 由于窝太菜了,实在是不会,所以在题解的帮助下过掉了这道题. 写此博客来整理一下思路 正文 传送 简化一下题意:现在有\(n\)行\(m\)列数,选\(k\)个数的合法方案需满足: 1.一行最多选一个 2.一列最多选\(\lfloor \frac{k}{2} \rfloor\)个数 当然,如果你在某一行里选了0,就相当于没有在这一行里选数 选一次对答案的贡献是你选的所有不为零的数的乘积.对于任意的\(k\),只要有合法方案,就能取. (希望没有把题目变得更复杂叭) 根据上面的要求,我们发现

CSP2019 树上的数 题解

题面 这是一道典型的部分分启发正解的题. 所以我们先来看两个部分分. Part 1 菊花图 这应该是除了暴力以外最好想的一档部分分了. 如上图(节点上的数字已省略),如果我们依次删去边(2)(1)(3)(4),那么操作完后2号点上的数字就会跑到1号点上,1号点数字会跑到3号点上,3号点数字跑到4号点上--依此累推.那么我们相当于把五个节点连成了一个环( 5 -> 2 -> 1 -> 3 -> 4 -> 5 ),每一个结点上的数字都会跑到环上的下一个结点上去,我们就是要求能使最

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

8.8联考题解

今天的T1让我怀疑我是不是在做奥赛题--这考的是什么知识点啊这个,会不会用绝对值函数? Evensgn 的债务 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Evensgn 有一群好朋友,他们经常互相借钱.假如说有三个好朋友A,B,C.A 欠 B 20 元,B 欠 C 20 元,总债务规模为 20+20=40 元.Evensgn 是个追求简约的人,他觉得这样的债务太繁杂了.他认为,上面的债务可以完全等价为 A 欠C20 元,B 既不欠别人,别人也不欠他.这样总债务规模就压缩到了 

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

(leetcode题解)Pascal&#39;s Triangle

Pascal's Triangle  Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Return [ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1] ] 题意实现一个杨辉三角. 这道题只要注意了边界条件应该很好实现出来,C++实现如下 vector<vector<int>> generate(int

2017ZZUACM省赛选拔试题部分题解----谨以纪念我这卡线滚粗的美好经历

写在前面: 其实心里有些小小的不爽又有点小小的舒畅,为啥捏?不爽当然是因为没被选拔上啦,舒畅捏则是因为没被选拔上反而让自己警醒,学长也提点很多很多."沉下去,然后一战成名"学长如是对我说,我很开心.其实这完全算不算是题解,只是我个人的一些小想法而已.而且到现在还有一题不会...让自己长点记性吧. 题目 A :聪明的田鼠 Time Limit: 1 Sec Memory Limit: 128 MB Description 田鼠MIUMIU来到了一片农田,农田可以看成是一个M*N个方格的矩

LeetCode-001题解

此题目摘自LeetCode001 Given an array of integers, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.