Codeforces Round #614 (Div. 2)
白天出门参加了同学聚会,大概到晚上八点多才定心地坐在电脑前打一些题目,智力–,手感–…
A
这道题卡死我了md…
盯着看了半个小时之后还是不知道哪里出了bug后转战BC,一年之后才修好这道题…
遇到水题做水的情况一定要保持好心态…及时换题 / 及时重构。
B
从感觉上来看如果第一场就全部阵亡和最后一场1个人阵容的情况获得加分是的一样的,再结合数据较小的情况可以猜测,最大的加分应该是调和级数之和。
当时没有想到证明办法,现在容易想到只需要考虑将t个人分成两批t1与t2,比较一次性阵亡和两次阵亡的分数即可,这是因为更多批的情况可以由两批人的情况推导得到。
从而我们做差法比较大小:
t
1
s
+
t
2
s
−
t
1
−
t
1
+
t
2
s
\frac{t_1}{s} + \frac{t_2}{s - t_1} - \frac{t_1 + t_2}{s}
st1+s−t1t2−st1+t2
进而易证猜想正确。
C
如果想要堵住2*n的道路,石头兄弟只有三种可能的组合情况。因而根据地面的状态变换维护总共会堵住道路的石头兄弟的个数即可。
补题
D
题目给出的线性变换的系数保证了该点会不断向右上角扩散,那么我们可以先把所有可能的点枚举出来。因为数据范围在1e16,所以在点的某个坐标超过2e16后就可以停止扩散了。之后,我们就可以在这个序列中进行搜索。
最开始是朴素的向前搜索,因为瞎了没有看到test2的情况(?),之后开始考虑怎么样搜索是最有效率的,最开始想到了搜到最前面之后再往后面搜索,但是不敢保证这样子一定是最好的,因为回头搜索浪费的部分在于重叠之处,即漂亮的数学表达式:
s
u
m
=
l
e
f
t
+
r
i
g
h
t
+
m
i
n
(
l
e
f
t
+
r
i
g
h
t
)
sum = left + right + min(left + right)
sum=left+right+min(left+right)
显然这就是最小的重叠部分,因为如果回头多次捡到的数据都可以用一次回头完成收集。
然后就想到了虚假的双指针??主要原因在于被漂亮的数学表达式迷惑了双眼,而没有多想一下导致写了很久的假算法。真实的双指针在于,选择向左和向右的扩散不是根据left和right目前的大小(假算法)(为什么我昨晚会写这种无厘头的算法),而是根据比较向左扩散和向右扩散后的新长度来决定到底往哪里扩散。
//dis[i]表示第i个节点和第i - 1个节点之间的距离
//cnt是两两距离之间d <= t的节点的容器大小
//因而dis数组的有效范围为1, cnt - 1
if(p >= 1){
if(q + 1 < cnt){
//两边都可以扩散则需比较大小后决定往什么方向走。
if(left + dis[p] <= right + dis[q + 1]){
left += dis[p];
p--;
}
else{
right += dis[q + 1];
q++;
}
}
//只有左边可以扩散
else{
left += dis[p];
p--;
}
}
else{
//只有右边可以扩散
if(q + 1 < cnt){
right += dis[q + 1];
q++;
}
else break;
}
另一种办法是:注意到每个点到下个点的距离都大于该点到起始节点的距离,我们可以直接从当前点不断往前走,最后如果仍有盈余再尽量的往后走。
没有想到这种办法还是因为题目做少了 && 数学感觉愈发差劲。很多数学题目都讲究适当猜测&勇于验证
,我大概地想了下这串坐标的变化后感觉可能涉及到二项式?就没有多考虑,但其实对于这种特殊位置的点多考虑考虑是很有益处的。
此外,这种办法其实就是利用性质而简化的双指针办法,实际的第一种双指针搜索算法即会以第二种策略类似的办法进行运行。