题解:P1737 [NOI2016] 旷野大计算

· · 题解

Task 1

给定 a,b,输出 -2a-2b6 行。

提取公因式变成 -2(a+b),可以用加法代替乘 2

Task 2

给定 a,求 \dfrac{1}{1+e^{17a}}6 行。

17 拆成右移 4 加自己,然后取反调用 S 即可。

Task 3

给定 a,求 \text{sign}(a)6 行。

注意到题目的精度限制远小于计算节点的精度限制,这启示我们可以使用极限的思想。

下文中我们使用 \text{inf} 替代一个足够大的值。具体取什么需要代入每个 task 算一下使得不会爆范围。

注意到 \lim_{x\to \infty}S(x)=0,S(0)=\dfrac{1}{2},\lim_{x\to \infty}S(x)=1,可以得到 g(x)=S(x\cdot \text{inf})=\begin{cases}0 & x<0\\ \dfrac{1}{2} & x=0\\ 1 & x>0\end{cases}。此时 \text{sign}(x)=2g(x)-1 就做完了。

Task 4

给定 a,求 |a|14 行。

因为输入不超过 9 位,我们可以制造 eps=10^{-10} 的偏差使得没有 a=0 的情况。

通过 Task 3 的启发,我们完全可以构造一个函数 x+g(x)\cdot \text{inf} 使得 x<0 仍然是 x,否则是 \text{inf}

这有什么好处?代入 S 就会发现 x<0S(x) 否则是 1

但是如果这么做我们就需要一个从 S(x) 还原回 x 的方法,在减掉 g(x)\cdot \text{inf} 后我们就得到了 \min(x,0)=\begin{cases}x & x<0\\ 0 & x=0\end{cases}

S 求导,有 S'(x)=\dfrac{e^{-x}}{(1+e^{-x})^2},代入 x=0 得到 S'(0)=\dfrac{1}{4},我们可以认为 S(\dfrac{x}{\text{inf}})=\dfrac{1}{4}\times \dfrac{x}{\text{inf}}+\dfrac{1}{2}

这样就可以从 S(x) 还原回 x 了。

最后 |x|=-2\min(x,0)+x 就做完了。

适当优化一下。

Task 5

输入 32 个二进制位上的值,转成数。95 行。

没啥好说的,优化一下 i=0 的右移就行了。

Task 6

输入数,转成 32 个二进制位上的值。190 行。

从高到低做,事实上我们只需要返回 [x\ge 2^i]

前面的 Task 3&4 已经教会大家 S 函数的打开方法了,直接求 S((x-2^i+10^{-10})\cdot \text{inf})

精细实现一下,一开始就加上 \text{eps} 并乘上 \text{inf}

Task 7

输入 a,b,求 a 异或 b605 行。

利用 Task 5&6,只需要做 a,b\in \{0,1\} 的情况。

转成 a+b-[a+b\ge 2]2,这个类似 Task 6 直接用 S 就可以了。

Task 8

输入 a,求 \dfrac{a}{10}7 行。

求出一个点 p 满足 S'(p)=\dfrac{1}{10},则答案是 \dfrac{S(p+\dfrac{x}{\text{inf}})-S(p)}{\text{inf}}

解出来 p=\ln(4+\sqrt{15})

Task 9

输入长度为 16 的序列,排序。3000 行。

考虑朴素的排序方法:

for(int i=0;i<16;++i)for(int j=i+1;j<16;++j)if(a[i]>a[j])swap(a[i],a[j]);

只需要求 \min(a,b)=a+\min(0,b-a),这个在 Task 4 已经会做了。

Task 10

给定 a,b,m,求 ab\bmod m2000 行。

一个自然的想法是使用快速幂的思想,这样我们需要实现在 a\in\{0,1\} 时的 ab。这里假定 b\ge 0

注意到 ab=\min(a\cdot \text{inf},b),套用前面的做法。

这个做法可能需要轻微卡一下常数。