【比赛】【SMOJ 2019.3.3】

十 年 O I 一 场 空 , 开 错 题 目 见 祖 宗 。


\(\mathrm{T1}\)

原来时间复杂度\(\Theta(6*10^8)\)的程序竟然能在\(4s\)之内跑完。

如没有括号限定的话,\(\mathrm{P}\)表示的是一个格子被一个矩形覆盖的概率

  1. 暴力推公式(\(\mathrm{P+(1-P)P+(1-P)^2P+(1-P)^3P......}\)

    \(80\ pts\)(精度被卡)

  2. 优化

    \(\mathrm{P(\texttt{至少有一次选中}) = 1-P(\texttt{一次都没有被选中})}\)

    所以上面的式子可以转化成

    \[\mathrm{1-P^{time}} \]

    \(time\)表示矩形个数)

    这样直接搞有\(90\ pts\)。(精度依然会被卡)

    然后就会想到快速幂(因为这样做乘法次数更少,精度丢失的也更少,时间复杂度反倒是其次的)。

    \(90\ pts\)做法里面的\(\mathrm{P^{time}}\)用快速幂实现就可以\(\mathrm{AC}\)了。

\(\mathrm {Code}\)

中间那一段可能有些长,其实就是用容斥原理计算一个点不被覆盖的概率。(算出四边,再减去四个角)


\(\mathrm{T2}\)

原来这就是多校联考题的真实水平。

首先我们可以发现对于一个建筑物,它的每一列都是独立的,所以我们可以先\(\mathrm{DP}\)每一列,再\(\mathrm{DP}\)整个场地。

然后因为每种颜色可以分开算,所以下面的题解默认只算看到蓝色的建筑物的方案数

1.\(\mathrm{\Theta(n^8)}\)做法

\(\mathrm{f[r][H][R][G][B]}\)为对于每一列,前\(r\)排放了\(\mathrm{R}\)个红色正方体,\(\mathrm{B}\)个蓝色正方体,\(\mathrm{G}\)个绿色正方体,且这一列方块的最高高度为\(\mathrm{H}\)的摆放方案数。

则我们可以每次考虑放多一整排进去,状态转移应该是这样:

\(\mathrm{\begin{cases} f[r+1][H][R+R'][G+G'][B+B'] = P(R',G',B') \times f[r][H][R][G][B] & (R'+G'+B' \leqslant H) \\f[r+1][R'+G'+B'][R+R'][G+G'][B+B'] = P(R',G',H-R'-G') \times f[r][H][R][G][B] & (R'+G'+B'>H) \end{cases}}\)

  • (上面的式子当中,第一种情况是\(R'+G'+B' \leqslant H\);第二种情况是\(R'+G'+B'>H\)。)
  • \(\mathrm{P(x,y,z)}\)表示三类数量分别为\(x,y,z\)的元素集的可重集排列数。
  • 第二条式子中出现\(\mathrm{P(R',G',R'+G'+B'-H) }\)是因为突出去的那些高于\(\mathrm{H}\)的正方体必须是蓝色,所以不用于计算可重集排列。

每一列算好之后就可以直接算整个场地的了。

\(\mathrm{F[l][R][G][B]}\)表示前\(l\)列,且用了\(\mathrm{R}\)个红色立方体、\(\mathrm{G}\)个绿色立方体、\(\mathrm{B}\)个蓝色立方体的方案数。

然后每次枚举上一列的\(\mathrm{RGB}\)即可。

\(\mathrm{Code}\)

坑点:

  1. 弄错数组的下标
  2. 逆元写错(当然其实可以直接写组合数)
  3. \(\mathrm{H-R'-G'}\)写错了……(不知道为啥写成了\(\mathrm{R'+G'+B'-H}\)……)

2.\(\mathrm{\Theta(n^6)}\)做法

其实我们可以放飞一下思想把红色和绿色这两种颜色混合起来

这样的话,在求\(f\)数组时我们就可以少枚举一个颜色,也就是少了两层循环了(每个颜色还要再枚举上一层的颜色个数)。

这样算出来的答案当然是错的,但是我们只要加上一步就对了:

把最终答案乘上\(\mathrm{C_{R+G}^{R}}\)(当然也可以乘上\(\mathrm{C_{R+G}^{G}}\))。

在知道红绿两种颜色的位置有多少不同方案之后,再乘上红绿颜色分布的组合数就是最终答案了。
时间复杂度\(\Theta(n^6*case)=\Theta(19.2\times 10^8)\)卡卡常数就可以过了。

\(\mathrm{Code}\)

坑点:数组没有开两倍!!!数组没有开两倍!!!数组没有开两倍!!!


但是,其实不需要卡常的。

我们并不需要每次都重新计算一次\(f\)数组,这个数组完全可以\(\Theta(n^6)\)预处理好,然后每次再像之前那样直接对\(\mathrm{F}\)数组进行\(\mathrm{DP}\)

这样的话,时间复杂度就变成\(\Theta(n^6+case\times n^5)=\Theta(10.54\times 10^8)\),亲测不需要卡常,可以直接过。

\(\mathrm{Code}\)


\(\mathrm{T3}\)

这一题倒在了建边上。

就是说,如果两个好点中间有很多好点的话,应该怎样确定这两个点之间至少应该加几个好点?

其实不需要想这么多,只需要点与点之间两两建边,边权是两个点之间的曼哈顿距离-1即可(如果两点之间要增加的好点数量可以更少的话,就一定可以通过走两点之间的其他好点来解决)。

然后在建边时特判一下这条边的两个端点是否在同一个位置上即可。

最后还要特判一下起点和终点是否在同一个位置。

话说为什么堆优化的Dijkstra会错,直接Dijkstra就对呢?

虽然直接搞的确快很多……

\(\mathrm{Code}\)

posted @ 2019-08-01 11:18  info___tion  阅读(149)  评论(0编辑  收藏  举报