“2018杭电ACM集训队单人排位赛- 3” 采用了这一套题,然后我成功地崩掉了…… 这里补上题解。
我对于最后两道题还暂时存在知识盲区,故这里只提供A∼H
前 8
道题的题解。
重现赛地址
A: Anagram
贪心。
对于A 串中的每一个字符,尽量使它变换次数最少,这样可以保证总变换次数最少。
完整参考代码
B: Bullet
二分图匹配+ 二分答案。
由每行、每列只能选一个怪物打,很容易想到将到二分图匹配的模型。我们把N
行和 N
列分别看作 N
个行节点和 N
个列节点,并且分别把它们看作二分图的左部和右部。
首先,我们能很容易地算出最大匹配数。只要Aij>0
(即怪物存在),我们就在左部 i
节点和右部 j
节点间建一条边。接着,我们跑一遍匈牙利匹配就可以算出最大匹配数 maxAns
。
接下来,我们来二分答案。记Aij
中最小的非零经验值为 L
,最大的经验值为 R
,那么显然答案区间即 [L,R]
。对于当前要判断可行性的最大经验值 mid
,我们只对 Aij≥mid
的点建边。由于题目要求的是在打怪数量最多前提下的最大经验值,我们只需要跑一遍匈牙利算法看一下在当前图中最大匹配数是否等于 maxAns
即可。
复杂度:O(N2logN)
完整参考代码
关于匈牙利算法的更多内容可以参考我的另一篇博文:浅谈匈牙利算法
另外,这道题有种贪心做法是错的,但是数据太水了……
C: Cities
贪心。
既然要建连通图,那么显然每个点的点权至少要被算上一次(因为它们都一定需要与另一个点相连)。我们希望与每个点相连的点的权值都最小,那么我们只需要找出权值最小的点,并让剩下的点都与它相连就好了。
完整参考代码
D: Dance
DFS+ 贪心。
要想让总魔法值最多,我们可以考虑尽量多选能带来最多魔法值的基础魔法来进行升级。我们可以首先对树DFS 一次,算出并记录每种基础魔法(也就是叶子节点)升级到最高魔法所能带来的魔法值。接下来我们对它从大到小进行排序,依次模拟升级过程(又是一次 DFS)。
这个做法貌似不能称作正解,因为如果构造一棵” 2N
个节点组成一条链,剩下 2N
个节点分别是前 2N
个节点的孩子” 这样子的树,貌似可以把这个做法卡掉…… 还是出题人太良心了。🙈
完整参考代码
E: Sequence
找规律。
要使删掉ai
后总好数最多,也就是要让删掉 ai
对好数数量造成的影响最小。这里我们分两种情况讨论:
- 如果删掉的 ai
本身是好数,就意味着存在 k<i, ak<ai
。换句话说,对于 j>i
且 aj>ai
的好数,就算把 ai
删掉了,依然有 k<j, ak<aj
使得 aj
满足好数的定义。因此删掉 ai
只会让总好数数量减一;
- 如果删掉的 ai
不是好数,那么删掉 ai
会影响满足 j>i, aj>ai
且在 aj
之前只有 ai<aj
的数。删掉 ai
会让总好数数量减去满足那样条件的数的数量。
方法一
至此,一种显而易见的解决方式是使用树状数组。
我们可以开两个树状数组。第一个树状数组用于获取ai
之前有多少个数小于 ai
,具体做法是读入 ai
后就将 ai
位置上的值改为 1
,这样获取前 ai−1
个位置的前缀和就是 ai
之前小于 ai
的数的个数了。我们记之为 pre[i]
。
第二个树状数组用于获取ai
后满足上面第二类情况里提到的满足特定条件的数有多少个。具体做法是倒着遍历一次序列,如果对于 ai
有 pre[i]=1
,就在树状数组中 ai
的位置上加 1
。如果 pre[i]=0
,就意味着 ai
不是好数,我们获取第 ai+1
个元素到第 n
个元素的后缀和就是满足特定条件元素的个数了。我们记之为 aff[i]
。 当然如果 pre[i]=0
的话就意味着 ai
是好数,那么 aff[i]=1
(删去它对总好数数量的影响为 1
)。
然后遍历一遍序列,找出aff[i]
最小的 ai
即可。
复杂度:O(NlogN)
完整参考代码
方法二
如果进一步观察,我们可以意识到我们其实没有必要记录pre[i]
… 我们只需要记录 ai
之前的最小值和次小值,同时扫一遍序列就行了。
我们记minVal
为当前最小值,secMinVal
为当前次小值。
- 如果 ai<minVal
,那么 minVal
变成了 ai
,secMinVal
变成了 minVal
;
- 如果 minVal≤ai<secMinVal
,那么意味着对于 ai
在其之前且比 ai
小的数只有 1
个。如果删去 minVal
必然会影响 ai
,因此 aff[minVal]++
。同时,又由于 ai
本身是个好数,因此删去 ai
也会少一个好数,aff[ai]++
;
- 如果 ai≥secMinVal
,那么 ai
本身是个好数,删去它会少一个好数,所以 aff[ai]++
。
然后遍历一遍序列,找出aff[i]
最小的 ai
即可。
复杂度:O(N)
完整参考代码
F: Four-tuples
容斥原理。
首先需要注意到的是,题目中事实上允许了x1=x3
或是 x2=x4
。
我们首先需要求得下列12
种情况的方案数:
- x1=x2=x3=x4
- x1=x2
- x2=x3
- x3=x4
- x4=x1
- x1=x2, x3=x4
- x2=x3, x4=x1
- x1=x2=x3
- x2=x3=x4
- x3=x4=x1
- x4=x1=x2
- x1=x2=x3=x4
那么根据容斥原理,我们最终所要求的答案即:1−(2+3+4)+(6+7)+(8+9+10+11)−3⋅12
。
完整参考代码
G: Game
变形的限制物品个数的统计方案数的背包问题。
首先,我们知道Nim 游戏先手必胜的局面为每堆石子数的异或和非 0
。那么显然,后手必胜的局面就是每堆石子数的异或和为 0
。那么本题实际上就是要求我们拿走不超过 d
堆石子使得剩下每堆石子数的异或和为 0
。
其次,我们需要知道异或运算的一个性质:a⊕a=0
。另外, a⊕b⊕a=b
。放在本题中,就意味着本题变为了:选出不超过 d
堆石子,使得选出石子异或和与所有石子异或和相等。
这样一来,我们不妨把所有石子异或和xorSum
视作背包容量,每堆石子视作物品,并且选用物品个数不能超过 d
。这就成了一个变形的背包问题,只不过总容量从加法和变成了异或和。
我们记录dp[i][j][k]
表示对于前 i
件物品,从中选取 j
件物品且已选取物品异或和为 k
时的总方案数。不难写出如下状态转移方程:
dp[i][j][k]={dp[i−1][j][k]dp[i−1][j][k]+dp[i−1][j−1][k⊕ai]j=0j>0
至于初始化,我们初始化dp[0][0][0]=1
。
完整参考代码
H: Dominoes
BFS。
既然保证图中只有一个空格,那么相比考虑骨牌的移动我们不妨考虑这个空格的移动。这样子一来这道题就显得很简单了。
完整参考代码