CSP-S2023 题解
密码锁(lock)
\(10^5\) 枚举所有可能答案,然后判断。
代码
1 | #include <bits/stdc++.h> |
消消乐(game)
首先发现对于同一个局面会有多个方案,比如 abbaabba
可以是 abbAAbba
也可以是 abbaAbbA
(两个 a
匹配,两个 A
匹配)。那么我们钦定只考虑 abbaAbbA
。怎么保证这一点呢?我们令 \(r(i)\) 等于最小的 \(j\) 满足 \([i, j]\) 这一段是可以被消除的。那么对于 abbaabba
来说,\(r(1)=4,r(5)=8\)。我们把 \([i, r(i)]\) 叫做 一段。
然后我们令 \(cnt(i)\) 表示从 \(i\) 开始最多往后走多少个段能走到 \(n\),也就是 \(cnt(i)=cnt(r(i)+1)+1\) 且 \(cnt(n+1)=0\),那么答案就是所有位置的 \(cnt\) 加起来。
然后考虑怎么算 \(r\)。容易发现 \(r(i)\) 其实等于最小的 \(j\) 使得 \([i+1, j-1]\) 能被消除且 \(s_i=s_j\),也就是从 \(i+1\) 开始往后一段一段跳,直到某一段结尾的后一个字符等于 \(s_i\)。那么我们对于每个 \(i\) 记 \(nxt(i,c)\) 表示从 \(i\) 开始往后一段一段跳,第一次跳到 \(c\) 时的下标,然后就可以计算 \(r\) 了。有了 \(r\),\(nxt\) 也是好转移的。
代码
1 | #include <bits/stdc++.h> |
结构体(struct)
直接模拟即可。
代码
1 | #include <bits/stdc++.h> |
种树(tree)
首先二分答案,那么我们要判断答案是否大于等于 \(m\)。首先对于每个点,我们可以计算出它最晚被种下去的时间 \(r_i\),这个可以二分(用 \(O(1)\) 似乎会被卡精度?)
先考虑没有“必须先种父亲再种儿子”的限制。我们令 \(s_i\) 表示有多少个 \(r_j\) 小于等于 \(i\),那么我们只需要判断 \(s_i\le i\)。正确性显然。(其实这个也可以贪心做,本质上是一样的)
现在考虑有这个限制怎么做。一个显然的事情是 \(r_u<r_v\)(\(v\) 是 \(u\) 的儿子),所以我们用这个限制来更新 \(r\)。然后就不需要考虑这个限制,直接按照上面的做法去做就好了。
为什么呢?令 \(t_i\) 表示一个合法方案中 \(i\) 实际上是第几天被种下去的,那么显然 \(t\) 互不相同且 \(t_i\le r_i\)。接下来我们只需要证明能找到一个 \(t'_i\) 满足“先种父亲再种儿子”的限制就行了。考虑调整法,如果对于一对拥有祖先关系的 \(u,v\),\(t_u>t_v\),那么我们有 \(t_v<t_u\le r_u<r_v\),那么我们可以将 \(t_u,t_v\) 交换,这样也是合法的。所以这个结论是对的。
代码
1 | #include <bits/stdc++.h> |