解题报告P1737 [NOI2016]旷野大计算

· · 题解

题目链接:P1737 [NOI2016]旷野大计算。

题目大意:只允许使用加、取反(添负号)、偏移(加减一个常数)、左右移位(乘或除以 2 的非负整数次幂)和神奇的 S(x) 函数来进行编程,造一台计算机构造一个计算网络,完成 10 项指定的计算任务。要求计算次数尽可能少。

其中 S(x) 函数为 {\large\frac{1}{1+e^{-x}}} ,是一个非线性的函数。

$y=S(x)$ 的图像: ![](https://cdn.luogu.com.cn/upload/image_hosting/t9az0ugn.png)

还有三个功能强大但是使用了会扣分的计算节点:比较节点、取两数最大值节点、乘法节点。

是一道提交答案题。

题目补充。

checker下载。

解法:构造答案

具体解法在各个 \text{task} 中介绍。

本题主要就是 10 个计算网络的构造。

核心思想就是:利用好 S(x) 的选择性。用 S(x) 来实现比较大小、取 01 ,制造分段函数,是这道题的关键所在。

必要的地方加一些小优化,以减少计算节点数。

本题解对解题过程中所用到的所有参数进行了我自认为比较详细地分析,希望这些分析能帮到阅读本题解的你。

\text{task1}

要求

输入:a,b

输入限制:|a|,|b|\leqslant10^9 ,小数部分不超过 9 位。

输出:-2a-2b

满分行数:6 行。

解决方法

送分点。

一般来说,我们都会想到提取公因数 -2 来减少运算次数。

一般来说,我们也都会想到用左移 1 位来计算乘 2 这个操作。

输出代码

if(n==1) { // 6行。 
    printf("I\n");
    printf("I\n");
    printf("+ 1 2\n");
    printf("< 3 1\n");
    printf("- 4\n");
    printf("O 5");
}

\text{task2}

要求

输入:a

输入限制:|a|\leqslant10^9 ,小数部分不超过 9 位。

输出:{\large\frac1{1+e^{17a}}}

满分行数:6 行。

解决方法

也是送分点。

观察输出的这个表达式的样子,显然这个点是要用 S(x) 函数的。

然后,注意到 17a=16a+a=2^4a+a ,所以我们可以用位移和加法来解决乘法了。

最后还得小心,S(x)={\large\frac1{1+e^{-x}}} ,还得将 17a 取反后使用 S 函数。

输出代码

if(n==2) { // 6行。 
    printf("I\n");
    printf("< 1 4\n");
    printf("+ 1 2\n");
    printf("- 3\n");
    printf("S 4\n");
    printf("O 5");
}

\text{task3}

要求

输入:a

输入限制:|a|\leqslant10^9 ,小数部分不超过 9 位。

输出:\text{sign}(a)=\begin{cases}-1,a<0\\\ \ \ 0,a=0\\\ \ \ 1,a>0\end{cases}

满分行数:6 行。

解决方法

不是送分了。开始有难度了。

没有除法,所以 {\large\frac a{|a|}} 泡汤了。况且求 |a| 是下一个 \text{task} 的任务,所以按道理说应该并不容易求。

使用比较语句的话,直接与 0 做比较,输出比较的值即可。可得 6 分。

可以用 a\times2^{-1000} 来造 0 。之后就可以用比较节点比较 a0 的大小。

但是如何使用基本操作来搞出答案呢?

我们是该想想怎么用 S 函数了。

回顾题目

其实,我们在读题面过程中,会发现计算过程中数据精度特别高:小数点后 90 位,整数部分绝对值不超过 10^{1000}

而最终 Special Judge 判定结果时,只要与标准答案的结果相差小于 10^{-9} 即可。

为什么给如此大的精度?尤其是整数部分,为何那么大?小数部分似乎也精确得过分了,这又是为什么?

而且,为什么 S 函数一定要用 {\large\frac 1{1+e^{-x}}} ?它有什么特殊之处吗?

观察发现,除了第二个测试点需要直接使用了 S(x) 来求出 {\large\frac1{1+e^{17a}}} 精确值之外,其余九个测试点表面上似乎都没有涉及到 S(x) 的使用。

出题人如果只是为了送分的话,为什么还要搞这么一个函数出来呢?这个函数真的没有其他作用了吗?

仔细想想,我们察觉到,好像确实没有哪个测试点需要使用 S(x) 的精确值了。所以,S(x) 的价值,难道是体现在其近似值上面了吗?

种种不同寻常的地方提醒着我们,这道题必然要涉及到极限的问题,即我们要通过某些操作,爆掉 90 位小数的精度,以此模拟取极限的过程。S 函数的特殊之处就在于,它在正负无穷大处都有极限,分别为 10 ,而 y=S(x) 图像上有唯一一个横纵坐标都是有理数的点 (0,{\large\frac12}) ,这也是 S(x) 的性质。进一步的, S(x)+S(-x)=1 ,同样是一条性质。

这就启示我们结合 \lim\limits_{x\to+\infty}S(x)=1\lim\limits_{x\to-\infty}S(x)=0 ,和 S(0)={\large\frac12} 这三条性质,来完成题目。

**所以 $S$ 函数与极限结合,应该是这道题考察的重点之一。**

怎么利用 S 函数呢?

既然要考虑极限,我们很容易想到,将 S 函数乘 2 ,再减去 1,所得恰好是个在 +\infty,-\infty 处极限分别为 +1,-1 的函数(即 {\large\frac2{1+e^{-x}}}-1)。而且在 x=0 处,取值也恰好为 0

这么看来,我们应该构造对了。

那么如何取极限?

简单,左移个百来位就可以变得很大了,只要 e^{-x} 爆了 90 位小数的精度,就能认为 S(x)\pm1 精确相等了。

大概多少位比较合适呢?

所以,左移 38 位即可。

输出代码

if(n==3) { // 6行。 
    printf("I\n");
    printf("< 1 38\n");
    printf("S 2\n");
    printf("< 3 1\n");
    printf("C 4 -1\n");
    printf("O 5");
}

\text{task4}

要求

输入:a

输入限制:|a|\leqslant10^9 ,小数部分不超过 9 位。

输出:|a| ,即 a 的绝对值。

满分行数:14 行。

解决方法

我的思路和第三个点的差不多。 首先我想到,$y=|x|$ 的图像关于 $y$ 轴对称,是个偶函数,而 $S(x)+S(-x)=1$ 告诉我们,$S(x)$ 图像关于 $(0,{\large\frac12})$ 对称。所以,怎么用 $S(x)$ 搞出一个偶函数来? $S(x)$ 在正负无穷处函数变化率**都**趋于零,这启示我们使用导数。 经过验算,我发现,$S'(x)={\large\frac{e^{-x}}{(1+e^{-x})^2}}$ 是个偶函数,且 $\lim\limits_{x\to\infty}S'(x)=0$ , $S(0)={\large\frac14}$ 。 虽然这个导数同 $|x|$ 一样,是个偶函数;但是 $|x|$ 在 $x$ 趋于无穷时是无穷,而 $S'(x)$ 在 $x$ 趋于无穷时却趋于 $0$ 。更糟的是,在 $x=0$ 的两侧,$|x|$ 的表达式可写成不一样的形式,即 $|x|=\begin{cases}\ \ \ x,x>0\\\ \ \ 0,x=0\\-x,x<0\end{cases}$ ,而 $S'(x)$ 不能。 所以这样看来, $S'(x)$ 并不能很好的模拟 $|x|$ 函数。即使能够用 $S'(x)$ 构造,怕也免不了一些乘法或者比较的操作。 虽然用 $S'(x)$ 直接构造 $|x|$ 的思路失败了,但引入 $S'(x)$ 来辅助解题确实是一个良好的尝试。 况且,**难道仅凭这一次尝试,就能断言 $S'(x)$ 毫无作用了吗?** 事实上导数真的很有用。 我们用不了 $S'(x)$ 的全部,我们只考虑 $S'(x)$ 在一点处的取值。 在解决第三个点时,我们使用了 $S(x)$ 的两个极限,那么做这个点时,不妨再考虑考虑,能不能使用 $S(0)={\large\frac12}$ 这个性质。 由 $S'(0)={\large\frac14}$ ,我们很容易写出 $S(x)$ 在 $x=0$ 处的切线:$y={\large\frac14}x+{\large\frac12}$ 。 由切线的意义可知,$y=S(x)$ 在 $x=0$ 附近与 $y={\large\frac14}x+{\large\frac12}$ 拟合得很好, 这样,我们就得到了一个关于 $x$ 的一次函数,且这个函数减去 ${\large\frac12}$ 再乘 $4$ 就能得到 $x$ 。 > **我们实际上得到的是一个从 $S(x)$ 得到 $x$ 的办法,这个办法很重要。** > > 其重要性在于,我们可以将获得 $x$ 的操作拆解为 $x\to S(x)\to x$ 两个步骤,从而向这两个步骤中添加进一些我们想要的条件。 > > 简言之,这个办法给了我们操作的空间,使得我们有了构造分段函数的可能。更本质的,使得我们有了构造条件语句的可能。 所以在这个测试点中,我们要从 $S({\large\frac x{+\infty}})$ 着手构造 $|x|$ ($S({\large\frac x{+\infty}})$ 这种写法不规范,但有助于理解)。 现在,再来考虑,如何将 $x$ 的正负区分开来。我们只有找到区分正负的办法,才可能构造出函数来模拟正负有别的 $|x|$ 函数。 区分正负的办法上面已经提到了,就是 $S(x)$ 在正负无穷大处的两个极限,分别为 $0,1$ 。 那么 $S(-x)$ 的极限就分别为 $1,0$ 。 我们要想办法让这两个截然不同的极限对我们构造的函数产生影响,使得我们构造的函数在 $x$ 正负符号不同时具有显著的差别。 设这个影响为 $t$ ,把这个影响加到 ${\large\frac {-x}{+\infty}}$ 上去(这就是对我们构造的函数产生影响的操作过程)。现在考虑 $S({\large\frac{-x}{+\infty}}+t)$ 。 我们需要让这个影响在 $x>0$ 时为无穷小(即:几乎没什么影响),在 $x<0$ 时足够大,大到能显著影响 $S({\large\frac{-x}{+\infty}}+t)$ 的值。 所谓显著影响 $S({\large\frac{-x}{+\infty}}+t)$ 的值,无非就是把 $S({\large\frac{-x}{+\infty}})$ 的值从 ${\large\frac12}$ 附近变成 $1$ ,就是说 $S({\large\frac{-x}{+\infty}}+t)\to1$ 。而减去 ${\large\frac12}$ 再乘 $4$ 后,这个影响作用的结果就变成了“使 $0$ 变成 $2$ ”。 这说明 $t$ 应该是个由 $1$ 搞出来的无穷大。比方说 $1\times2^{64}$ (这里未必就是 $64$ 这个数,还可以更大,而且这里的 $1$ 不是确切的数字 $1$ ,它只是表示一个趋近于 $1$ 但永远不等于 $1$ 的变量)。 我们把这个大小为 $2$ 的影响稍微调整一下,通过位移**调整到**和原来的影响 $t$ 大小相当的地步。这样,我们就可以通过减去 $t$ 来消除多余的影响,来构造出一个在 $x<0$ 时取 $0$ ,$x>0$ 时取 $x$ 的函数。 即 $\begin{cases}0,x<0\\x,x>0\end{cases}$ 。 这下就好办了。让这个函数乘 $2$ ,再减去 $x$ ,就得到 $|x|$ 啦! 上面的思考太抽象了,我们来重新整理一下思路: + 首先,计算 $t=S(-x\times2^{q})\times2^{p}=\begin{cases}0\ \ ,x>0\\2^p,x<0\end{cases}$ , $PS.$ 这是 $y=S(x\times2^{41})\times2^{178}$ 的图像: ![](https://cdn.luogu.com.cn/upload/image_hosting/4judcfh2.png) 这里就要求 $S(-x\times2^q)$ 能够与 $0$ 或 $1$ 近似相等, + 于是令 $x=-10^{-9}$ , 只需有 ${\large\frac1{1+e^{-2^q10^{-9}}}}>1-10^{-90}$ , 利用上面的近似公式,可知 $1-e^{-2^q10^{-9}}<{\large\frac1{1+e^{-2^q10^{-9}}}}$ , 只需要 $1-e^{-2^q10^{-9}}>1-10^{-90}$ , 即 $2^q10^{-9}>90\ln10$ , 即 $q>\log_2(9\times10^{10}\ln10)\approx37.59$ , 所以 $q\geqslant38$ 。 + 再令 $x=10^{-9}$ , 只需有 ${\large\frac1{1+e^{2^q10^{-9}}}}<10^{-90}$ , 即 $e^{2^q10^{-9}}>10^{90}-1$ , 只需 $e^{2^q10^{-9}}>10^{90}$ , 即 $2^q10^{-9}>90\ln10$ ,与上种情况同解, 所以 $q\geqslant38$ 。 这样,在跳蚤看来,$S(-x\times2^q)$ 与 $0$ 或 $1$ 是精确相等的。 $PS.$ 这是 $y=S(x\times2^{41})$ 的图像: ![](https://cdn.luogu.com.cn/upload/image_hosting/nbm41bur.png) + 然后,计算 $S({\large\frac{-x}{2^{k}}}+t)=\begin{cases}{\large\frac14}{\large\frac{-x}{2^{k}}}+{\large\frac12},x>0\\\\1\ \ \ \ \ \ \ \ \ \ \ \ \ ,x<0\end{cases}$ , *** **声明:为了方便书写,以下所有表达式 $x$ 均表示取反后的 $x$ ,但表达式中表示条件的 $x$ 仍然为未取反的 $x$ 。** **声明:为了方便书写,以下所有表达式 $x$ 均表示取反后的 $x$ ,但表达式中表示条件的 $x$ 仍然为未取反的 $x$ 。** **声明:为了方便书写,以下所有表达式 $x$ 均表示取反后的 $x$ ,但表达式中表示条件的 $x$ 仍然为未取反的 $x$ 。** *** + 这里要求 $x>0$ 时, $({\large\frac14}{\large\frac x{2^{k}}}+{\large\frac12})-S({\large\frac x{2^{k}}})<10^{-90}$ ,不妨取 $x=10^9$ ,即 ${\large\frac{10^9}{4\times2^k}+{\large\frac12}-{\large\frac1{1+e^{-2^{-k}10^9}}}}<10^{-90}
为了方便书写,设 ${\large\frac{10^9}{2^k}}=t$ ,

则上式为 ${\large\frac{t}{4}}+{\large\frac12}-{\large\frac1{1+e^{-t}}}<10^{-90}$

即 ${\large\frac{t}{4}}-{\large\frac{1-e^{-t}}{2(1+e^{-t})}}<10^{-90}$ ,

即 ${\large\frac{t}{2}}-{\large\frac{e^{t}-1}{e^{t}+1}}<2\times10^{-90}$ ,

因为 $t>0$ 时, ${\large\frac{e^{t}-1}{e^{t}+1}}>{\large\frac{t}{e^t+1}}$ ,

> 这里用到了不等式 $e^t>t+1$ ,该不等式可用导数的相关知识来证明。

所以只需 ${\large\frac{t}{2}}-{\large\frac{t}{e^t+1}}<2\times10^{-90}$ ,

即 $t{\large\frac{e^t-1}{2(e^t+1)}}<2\times10^{-90}$ ,

因为 $t>0$ 时, ${\large\frac{1}{(e^t+1)}}<{\large\frac{1}{2}}$ ,

所以只需 ${\large\frac{t(e^t-1)}{4}}<2\times10^{-90}$ ,即 $t(e^t-1)<8\times10^{-90}$ ,

还是利用 $e^t>t+1$ ,欲使上式成立,至少需要满足 $(e^t-1)^2<8\times10^{-90}$ ,

解得 $t<\ln(2\sqrt2\times10^{-45}+1)$ ,

(其实只是个必要条件)

把 $t={\large\frac{10^9}{2^k}}$ 代入,得 ${\large\frac{10^9}{2^k}}<\ln(2\sqrt2\times10^{-45}+1)$ ,

即 $2^{k}>{\large\frac{10^9}{\ln(2\sqrt2\times10^{-45}+1)}}$ ,

即 $k>9\log_210-\log_2(\ln(2\sqrt2\times10^{-45}+1))\approx177.884$ ,

所以 $k\geqslant178$ 。

当然 $k$ 不能取太大,否则会造成 ${\large\frac{x}{2^k}}$ 的精度下降,只需 $10^{-9}>2^k\times10^{-90}$ ,解得 $k\leqslant269$ 。

而且,我所采用的放缩方式,其放缩幅度还是很大的,所以 $178$ 的下界很松。但上界 $269$ 是严格的。

得到 |x| 啦。

这里面还有一个小问题,那就是 t 在负数和零处的取值是不同的,具体而言 t=S(-x\times2^{q})\times2^{p}\approx\begin{cases}2^{p}\ \ \ ,x<0\\2^{p-1},x=0\\0\ \ \ \ \ \ ,x>0\end{cases} ,这样的话,在 x=0 处计算得到 (S({\large\frac x{2^{k}}}+t)-{\large\frac12})\times2^{k+3} 后,消除 t 的影响时,需要减去 2t 才行。

但若是特判零的话,势必要增加很多节点,相当麻烦。

我们想到 |x|=\begin{cases}x\ \ \ ,x\geqslant0\\-x,x<0\end{cases} ,所以我们能不能把 x=0x>0 合并到一起运算呢(或者和 x<0 合并到一起运算)?

可以的。

那么,怎么合并呢?

由于 x 小数部分不超过 9 位,所以给 x 加上一个很小的常数 \varepsilon=10^{-10} 即可。这样,当 x\neq0 时, x 原本的符号不改变;x=0 时,x 变成了正数 10^{-10}

相应的我们需要调整一下参数,

此时需要满足 q>\log_2(9\times10^{11}\ln10)\approx40.914

所以 q\geqslant41

> $PS.
$2^{40.914}\approx2,071,768,581,099.5056016$ 。 差距还是蛮大的。

我们不妨取 q=41,k=178,p=180

这样一写,发现至少要 15 行,究其原因,是我们需要对 x,t 各做一次取反。所以考虑不对 x,t 取反,而对 (S({\large\frac x{2^{k}}}+t)-{\large\frac12})\times2^{k+3} 取反的写法。

声明到此结束。

t=S({\large\frac{x+\varepsilon}{2^q}})\times2^p=\begin{cases}2^p,x\geqslant0\\0\ \ ,x<0\end{cases} ,我们容易得到,答案就是:

这样就只需要取一次反,恰好 $14$ 行。 ## 输出代码 $6$ 分代码,使用乘法节点: ```cpp if(n==4) { // 6分代码。 printf("I\n"); printf("< 1 38\n"); printf("S 2\n"); printf("< 3 1\n"); printf("C 4 -1\n"); printf("* 1 5\n"); printf("O 6"); } ``` $9$ 分代码,$15$ 行: ```cpp if(n==4) { // 15行。 printf("I\n"); printf("C 1 0.0000000001\n"); printf("- 2\n"); printf("< 3 41\n"); printf("S 4\n"); printf("< 5 180\n"); printf("> 1 178\n"); printf("+ 7 6\n"); printf("S 8\n"); printf("C 9 -0.5\n"); printf("< 10 181\n"); printf("- 6\n"); printf("+ 12 11\n"); printf("+ 13 3\n"); printf("O 14"); } ``` 以及满分代码,$14$ 行: ```cpp if(n==4) { // 14行。 printf("I\n"); printf("C 1 0.0000000001\n"); printf("< 2 41\n"); printf("S 3\n"); printf("< 4 180\n"); printf("> 1 178\n"); printf("+ 6 5\n"); printf("S 7\n"); printf("C 8 -0.5\n"); printf("< 9 181\n"); printf("- 10\n"); printf("+ 11 5\n"); printf("+ 12 1\n"); printf("O 13"); } ``` # $\text{task5}

要求

输入:a_1,a_2,\cdots,a_{32}

输入限制:a_1,a_2,\cdots,a_{32}\in\{0,1\}

输出:把 a_1,a_2,\cdots,a_{32} 从左到右看成一个二进制整数,高位在左,低位在右,输出该整数的值。

满分行数:95 行。

解决方法

终于,

熬过上题后,

再一次遇到送分点。

按照位数加权再相加即可。

只要弄对行数,就没有任何问题。

显然没有更优解。因为没有能合并的项。

秦九韶算法可减少乘法次数,但现在是做位移。

输出代码

if(n==5) { // 95行。 
    for(int i=1;i<=32;++i) printf("I\n");
    for(int i=1;i<=31;++i) printf("< %d %d\n",i,32-i);
    printf("+ 63 32\n");
    for(int i=64;i<=93;++i) printf("+ %d %d\n",i,i-31);
    printf("O 94");
}

\text{task6}

要求

输入:a

输入限制:0\leqslant|a|<2^{32}a 为整数。

输出:输出 32 个整数,从高位到低位输出 a 的二进制表示(不足 32 位的在高位补 0)。

满分行数:190 行。

解决方法

输出的是 a 的二进制表示,肯定只包含 01

可以想到,这题又要用 S(x) 函数来搞出 01

S(x) 获得 01 要求自变量 x 有正负两种取值,所以我们构造这个正负的取值。

考虑 a2 进制的最高位,即第 31 位,很显然,若这一位为 1 ,则 a\geqslant2^{31} ;若这一位为 0 ,则 a<2^{31} ,于是我们减去 2^{31} 就能搞出正负不同的取值了。

同样的,我们需要加上 10^{-10} 来保证 S(x) 不会恰好取到 {\large\frac12}

由此我们发现,S(x) 函数能够用来比较变量和某个常数之间的大小关系。

我们至少需要将减去 2^{31} 后的数左移 41 位以爆掉精度。这是 \text{task4} 中计算过的。

所以我们计算最高位时就输出 S((a-2^{31})\times2^{41}) ,即 S(a\times2^{41}-2^{72})

我们得到 01 后,把它左移恰当位数后再从原数中减去,得到 a' ,就可以继续对下一位搞出正负两种取值了。第二高位答案就是 S((a'-2^{30})\times2^{41})=S(a'\times2^{41}-2^{71})

诶?还是乘 41 哎!

于是我们发觉,可以一开始就将 a 左移 41 位,以减少计算节点数。

但这样搞就需要我们计算出 2^{42}2^{72}31 个幂次方了。计算这些幂次方可以用高精(三个 long\ long 拼起来就足够了),可以用 pow 函数,也可以用计算器来手动模拟压位高精。

当然最靠谱的方法就是用题目提供的 checker 来计算。写好计算节点,输入 1 ,让 checker 自己跑出来 2^{42}2^{72} 。省时省力。

总之是可以预处理的。

总体思路大概就是这样了。

然后发现,我们可以把“加上 10^{-10}” 这个操作放到一开始,这样就不必每次循环都多使用一个计算节点了。

计算一下节点的总数,循环里至少需要 “C,S,O,-,<,+” 六个节点,把最后一位拿出来直接处理的话,就只需要 31 次循环。

满分行数是 190 ,恰好是 31\times6+4 ,所以每次循环用 6 个节点应该确实是正确的。

但我们实际操作一下就会发现,按照以上所说的做法,需要写 191 行,能拿 9 分。

说明有一行可以被优化掉。

仔细想想,发现我们可以把“加上 10^{-10}” 这个操作放到 C 操作里,每次减去的不要刚好是 2 的幂次,而是 2 的幂次减去 10^{-10}\times2^{41}\approx219.902\approx220 ,这样就能减少最开始的偏移节点,从而将计算节点数减少到 190

为什么是 2 的幂次减去 220 而不是加上 220 呢?

因为减去 2 的幂次后等于零意味着这一位本来是 1 ,所以我们应该让这一位经过 220 调整完后是个正数。所以应该是 2 的幂次减去 220

于是我们把偏移的参数调整一下,通通减去 220 即可。

现在是 190 行。

输出代码

```cpp if(n==6) { // 191行。 char s[32][25]={ "\0", "4398046511104", // 2^42 "8796093022208", // 2^43 "17592186044416", // 2^44 "35184372088832", // 2^45 "70368744177664", // 2^46 "140737488355328", // 2^47 "281474976710656", // 2^48 "562949953421312", // 2^49 "1125899906842624", // 2^50 "2251799813685248", // 2^51 "4503599627370469", // 2^52 "9007199254740992", // 2^53 "18014398509481984", // 2^54 "36028797018963968", // 2^55 "72057594037927936", // 2^56 "144115188075855872", // 2^57 "288230376151711744", // 2^58 "576460752303423488", // 2^59 "1152921504606846976", // 2^60 "2305843009213693952", // 2^61 "4611686018427387904", // 2^62 "9223372036854775808", // 2^63 "18446744073709551616", // 2^64 "36893488147419103232", // 2^65 "73786976294838206464", // 2^66 "147573952589676412928", // 2^67 "295147905179352825856", // 2^68 "590295810358705651712", // 2^69 "1180591620717411303424", // 2^70 "2361183241434822606848", // 2^71 "4722366482869645213696", // 2^72 }; printf("I\n"); printf("C 1 0.0000000001\n"); printf("< 2 41\n"); for(int i=4,j=31;j>=1;--j,i+=6) { printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("O %d\n",i+1); printf("- %d\n",i+2); printf("< %d %d\n",i+3,41+j); printf("+ %d %d\n",i+4,i-1); } printf("> 189 41\n"); printf("O 190"); } ``` 满分,$190$ 行: ```cpp if(n==6) { // 190行。 char s[32][25]={ "\0", "4398046510884", // 2^42-220 "8796093021988", // 2^43-220 "17592186044196", // 2^44-220 "35184372088612", // 2^45-220 "70368744177444", // 2^46-220 "140737488355108", // 2^47-220 "281474976710436", // 2^48-220 "562949953421092", // 2^49-220 "1125899906842404", // 2^50-220 "2251799813685028", // 2^51-220 "4503599627370249", // 2^52-220 "9007199254740772", // 2^53-220 "18014398509481764", // 2^54-220 "36028797018963748", // 2^55-220 "72057594037927716", // 2^56-220 "144115188075855652", // 2^57-220 "288230376151711524", // 2^58-220 "576460752303423268", // 2^59-220 "1152921504606846756", // 2^60-220 "2305843009213693732", // 2^61-220 "4611686018427387684", // 2^62-220 "9223372036854775588", // 2^63-220 "18446744073709551396", // 2^64-220 "36893488147419103012", // 2^65-220 "73786976294838206244", // 2^66-220 "147573952589676412708", // 2^67-220 "295147905179352825636", // 2^68-220 "590295810358705651492", // 2^69-220 "1180591620717411303204", // 2^70-220 "2361183241434822606628", // 2^71-220 "4722366482869645213476", // 2^72-220 }; printf("I\n"); printf("< 1 41\n"); for(int i=3,j=31;j>=1;--j,i+=6) { printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("O %d\n",i+1); printf("- %d\n",i+2); printf("< %d %d\n",i+3,41+j); printf("+ %d %d\n",i+4,i-1); } printf("> 188 41\n"); printf("O 189"); } ``` # $\text{task7}

要求

输入:a,b

输入限制:0\leqslant a,b<2^{32}a,b 均为整数。

输出:a,b 按位异或后的结果。

满分行数:605 行。

解决方法

因为是按位异或,所以必然需要把每一位都先取出来。这就用到 \text{task6} 的代码了。

而按位异或完得到的 32 位二进制数还需要转化成十进制数才能输出,所以这又用到 \text{task5} 的代码了。

这就已经用掉大约 2\times5\times31+62=372 个计算节点,满分是 605 个计算节点,所以我们大约需要用 7 个计算节点来完成每一位上的异或操作。

异或还是涉及到 01 的运算,我们当然离不开 S(x) 函数啦。

输出代码

```cpp if(n==7) { // 663行。 char s[32][25]={ "\0", "4398046510884", // 2^42-220 "8796093021988", // 2^43-220 "17592186044196", // 2^44-220 "35184372088612", // 2^45-220 "70368744177444", // 2^46-220 "140737488355108", // 2^47-220 "281474976710436", // 2^48-220 "562949953421092", // 2^49-220 "1125899906842404", // 2^50-220 "2251799813685028", // 2^51-220 "4503599627370249", // 2^52-220 "9007199254740772", // 2^53-220 "18014398509481764", // 2^54-220 "36028797018963748", // 2^55-220 "72057594037927716", // 2^56-220 "144115188075855652", // 2^57-220 "288230376151711524", // 2^58-220 "576460752303423268", // 2^59-220 "1152921504606846756", // 2^60-220 "2305843009213693732", // 2^61-220 "4611686018427387684", // 2^62-220 "9223372036854775588", // 2^63-220 "18446744073709551396", // 2^64-220 "36893488147419103012", // 2^65-220 "73786976294838206244", // 2^66-220 "147573952589676412708", // 2^67-220 "295147905179352825636", // 2^68-220 "590295810358705651492", // 2^69-220 "1180591620717411303204", // 2^70-220 "2361183241434822606628", // 2^71-220 "4722366482869645213476", // 2^72-220 }; char s15[]="3298534883328",s05[]="1099511627776"; printf("I\n"); printf("I\n"); printf("< 1 41\n"); printf("< 2 41\n"); for(int i=5,j=31;j>=1;--j,i+=19) { printf("C %d -%s\n",i-2,s[j]); // i printf("C %d -%s\n",i-1,s[j]); // i+1 printf("S %d\n",i); // i+2 printf("S %d\n",i+1); // i+3 printf("+ %d %d\n",i+2,i+3); // i+4 printf("C %d -0.5\n",i+4); // i+5 printf("C %d -1.5\n",i+4); // i+6 printf("< %d 41\n",i+5); // i+7 printf("< %d 41\n",i+6); // i+8 printf("S %d\n",i+7); // i+9 printf("S %d\n",i+8); // i+10 printf("- %d\n",i+10); // i+11 printf("+ %d %d\n",i+11,i+9); // i+12 printf("- %d\n",i+2); // i+13 printf("- %d\n",i+3); // i+14 printf("< %d %d\n",i+13,41+j); // i+15 printf("< %d %d\n",i+14,41+j); // i+16 printf("+ %d %d\n",i+15,i-2); // i+17 printf("+ %d %d\n",i+16,i-1); // i+18 } printf("+ 592 593\n"); // 594 printf("C 594 -%s\n",s05); printf("C 594 -%s\n",s15); printf("S 595\n"); printf("S 596\n"); printf("- 598\n"); printf("+ 599 597\n"); // 600 for(int i=17,j=31;j>=1;--j,i+=19) printf("< %d %d\n",i,j); // 601~631 for(int i=601,j=631;i<=630;++j,++i) printf("+ %d %d\n",i,j); // 632~661 printf("+ 661 600\n"); printf("O 662"); } ``` 满分,$603$ 行: ```cpp if(n==7) { // 603行。 char s[32][25]={ "\0", "4398046510884", // 2^42-220 "8796093021988", // 2^43-220 "17592186044196", // 2^44-220 "35184372088612", // 2^45-220 "70368744177444", // 2^46-220 "140737488355108", // 2^47-220 "281474976710436", // 2^48-220 "562949953421092", // 2^49-220 "1125899906842404", // 2^50-220 "2251799813685028", // 2^51-220 "4503599627370249", // 2^52-220 "9007199254740772", // 2^53-220 "18014398509481764", // 2^54-220 "36028797018963748", // 2^55-220 "72057594037927716", // 2^56-220 "144115188075855652", // 2^57-220 "288230376151711524", // 2^58-220 "576460752303423268", // 2^59-220 "1152921504606846756", // 2^60-220 "2305843009213693732", // 2^61-220 "4611686018427387684", // 2^62-220 "9223372036854775588", // 2^63-220 "18446744073709551396", // 2^64-220 "36893488147419103012", // 2^65-220 "73786976294838206244", // 2^66-220 "147573952589676412708", // 2^67-220 "295147905179352825636", // 2^68-220 "590295810358705651492", // 2^69-220 "1180591620717411303204", // 2^70-220 "2361183241434822606628", // 2^71-220 "4722366482869645213476", // 2^72-220 }; printf("I\n"); // 1。 printf("I\n"); // 2。 printf("< 1 41\n"); // 3。 for(int i=4,j=31;j>=1;--j,i+=5) { // 4~158。 printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("- %d\n",i+1); printf("< %d %d\n",i+2,41+j); printf("+ %d %d\n",i+3,i-1); } printf("> 158 41\n"); // 159。 printf("< 2 41\n"); // 160。 for(int i=161,j=31;j>=1;--j,i+=5) { // 161~315。 printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("- %d\n",i+1); printf("< %d %d\n",i+2,41+j); printf("+ %d %d\n",i+3,i-1); } printf("> 315 41\n"); // 316。 for(int i=5,j=1;j<=31;++j,i+=5) { // 317~347。 printf("+ %d %d\n",i,i+157); } printf("+ 159 316\n"); // 348。 for(int i=317,j=1;j<=32;++j,++i) { // 349~380。 printf("C %d -1.5\n",i); } for(int i=349,j=1;j<=32;++j,++i) { // 381~412。 printf("< %d 41\n",i); } for(int i=381,j=1;j<=32;++j,++i) { // 413~444。 printf("S %d\n",i); } for(int i=413,j=1;j<=32;++j,++i) { // 445~476。 printf("< %d 1\n",i); } for(int i=445,j=1;j<=32;++j,++i) { // 477~508。 printf("- %d\n",i); } for(int i=477,j=1;j<=32;++j,++i) { // 509~540。 printf("+ %d %d\n",i,i-160); } for(int i=509,j=31;j>=1;--j,++i) { // 541~571。 printf("< %d %d\n",i,j); } for(int i=540,j=1;j<=31;++j,++i) { // 572~602。 printf("+ %d %d\n",i,i+31); } printf("O 602\n"); } ``` 满分,$542$ 行: ```cpp if(n==7) { // 542行。 char s[32][25]={ "\0", "4398046510884", // 2^42-220 "8796093021988", // 2^43-220 "17592186044196", // 2^44-220 "35184372088612", // 2^45-220 "70368744177444", // 2^46-220 "140737488355108", // 2^47-220 "281474976710436", // 2^48-220 "562949953421092", // 2^49-220 "1125899906842404", // 2^50-220 "2251799813685028", // 2^51-220 "4503599627370249", // 2^52-220 "9007199254740772", // 2^53-220 "18014398509481764", // 2^54-220 "36028797018963748", // 2^55-220 "72057594037927716", // 2^56-220 "144115188075855652", // 2^57-220 "288230376151711524", // 2^58-220 "576460752303423268", // 2^59-220 "1152921504606846756", // 2^60-220 "2305843009213693732", // 2^61-220 "4611686018427387684", // 2^62-220 "9223372036854775588", // 2^63-220 "18446744073709551396", // 2^64-220 "36893488147419103012", // 2^65-220 "73786976294838206244", // 2^66-220 "147573952589676412708", // 2^67-220 "295147905179352825636", // 2^68-220 "590295810358705651492", // 2^69-220 "1180591620717411303204", // 2^70-220 "2361183241434822606628", // 2^71-220 "4722366482869645213476", // 2^72-220 }; printf("I\n"); // 1。 printf("I\n"); // 2。 printf("+ 1 2\n"); // 3。 printf("< 1 41\n"); // 4。 for(int i=5,j=31;j>=1;--j,i+=5) { // 5~159。 printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("- %d\n",i+1); printf("< %d %d\n",i+2,41+j); printf("+ %d %d\n",i+3,i-1); } printf("> 159 41\n"); // 160。 printf("< 2 41\n"); // 161。 for(int i=162,j=31;j>=1;--j,i+=5) { // 162~316。 printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("- %d\n",i+1); printf("< %d %d\n",i+2,41+j); printf("+ %d %d\n",i+3,i-1); } printf("> 316 41\n"); // 317。 for(int i=6,j=1;j<=31;++j,i+=5) { // 318~348。 printf("+ %d %d\n",i,i+157); } printf("+ 160 317\n"); // 349。 for(int i=318,j=1;j<=32;++j,++i) { // 350~381。 printf("C %d -1.5\n",i); } for(int i=350,j=1;j<=32;++j,++i) { // 382~413。 printf("< %d 41\n",i); } for(int i=382,j=1;j<=32;++j,++i) { // 414~445。 printf("S %d\n",i); } for(int i=414,j=32;j>=1;--j,++i) { // 446~477。 printf("< %d %d\n",i,j); } for(int i=446,j=1;j<=32;++j,++i) { // 478~509。 printf("- %d\n",i); } printf("+ 478 3\n"); // 510。 for(int i=479,j=1;j<=31;++j,++i) { // 511~541。 printf("+ %d %d\n",i,i+31); } printf("O 541"); } ``` 满分,$539$ 行: ```cpp if(n==7) { // 539行。 // (a+b)-sum{(a[i]+b[i]==2)<<i+1} 能省掉二进制转十进制的节点。 char s[32][25]={ "\0", "4398046510884", // 2^42-220 "8796093021988", // 2^43-220 "17592186044196", // 2^44-220 "35184372088612", // 2^45-220 "70368744177444", // 2^46-220 "140737488355108", // 2^47-220 "281474976710436", // 2^48-220 "562949953421092", // 2^49-220 "1125899906842404", // 2^50-220 "2251799813685028", // 2^51-220 "4503599627370249", // 2^52-220 "9007199254740772", // 2^53-220 "18014398509481764", // 2^54-220 "36028797018963748", // 2^55-220 "72057594037927716", // 2^56-220 "144115188075855652", // 2^57-220 "288230376151711524", // 2^58-220 "576460752303423268", // 2^59-220 "1152921504606846756", // 2^60-220 "2305843009213693732", // 2^61-220 "4611686018427387684", // 2^62-220 "9223372036854775588", // 2^63-220 "18446744073709551396", // 2^64-220 "36893488147419103012", // 2^65-220 "73786976294838206244", // 2^66-220 "147573952589676412708", // 2^67-220 "295147905179352825636", // 2^68-220 "590295810358705651492", // 2^69-220 "1180591620717411303204", // 2^70-220 "2361183241434822606628", // 2^71-220 "4722366482869645213476", // 2^72-220 }; char s15[]={"3298534883328"}; printf("I\n"); // 1。 printf("I\n"); // 2。 printf("+ 1 2\n"); // 3。 printf("< 1 41\n"); // 4。 for(int i=5,j=31;j>=1;--j,i+=5) { // 5~159。 printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("- %d\n",i+1); printf("< %d %d\n",i+2,41+j); printf("+ %d %d\n",i+3,i-1); } printf("< 2 41\n"); // 160。 for(int i=161,j=31;j>=1;--j,i+=5) { // 161~315。 printf("C %d -%s\n",i-1,s[j]); printf("S %d\n",i); printf("- %d\n",i+1); printf("< %d %d\n",i+2,41+j); printf("+ %d %d\n",i+3,i-1); } for(int i=6,j=1;j<=31;++j,i+=5) { // 316~346。 printf("+ %d %d\n",i,i+156); } printf("+ 159 315\n"); // 347。 for(int i=316,j=1;j<=31;++j,++i) { // 348~378。 printf("C %d -1.5\n",i); } for(int i=348,j=1;j<=31;++j,++i) { // 379~409。 printf("< %d 41\n",i); } printf("C 347 -%s\n",s15); // 410。 for(int i=379,j=1;j<=32;++j,++i) { // 411~442。 printf("S %d\n",i); } for(int i=411,j=32;j>=1;--j,++i) { // 443~474。 printf("< %d %d\n",i,j); } for(int i=443,j=1;j<=32;++j,++i) { // 475~505。 printf("- %d\n",i); } printf("+ 475 3\n"); // 506。 for(int i=476,j=1;j<=31;++j,++i) { // 507~538。 printf("+ %d %d\n",i,i+31); } printf("O 538"); } ``` # $\text{task8}

要求

输入:a

输入限制:|a|\leqslant10^9 ,小数部分不超过 9 位。

输出:{\large\frac a{10}}

满分行数:7 行。

解决方法

与二进制无关了,所以用不到 S(x) 的选择性了。

通过 node8.ans ,我们知道,满分计算机是 7 个计算节点!

这说明我们需要用一些奇技淫巧了。

下面讲正解。

我们考虑如何正确使用 S(x) 函数来完成除以 10 的操作。

回想我们完成 \text{task4} 时对 S(x) 导数的思考,以及我们使用 S(x)x=0 处的切线来获取一条近似直线的操作,

那么我们是否可以找到 S(x) 上一条斜率为 {\large\frac1{10}} 的切线呢?

我们知道, S'(x)={\large\frac{e^{-x}}{(1+e^{-x})^2}}\in(0,{\large\frac14}] ,且 S'(x)R 上的连续函数、偶函数,

所以 \exists\ \xi\in(0,+\infty) , 使得 S'(\xi)={\large\frac1{10}}

我们可以写出 S(x) 在这点的切线方程,为 y=S'(\xi)(x-\xi)+S(\xi)

y={\large\frac1{10}}(x-\xi)+S(\xi)

由切线的意义知,当 x\xi 非常接近时,S(x) 近似等于 S'(\xi)(x-\xi)+S(\xi)={\large\frac1{10}}(x-\xi)+S(\xi)

所以我们将输入的 a 右移 k 位(造一个微小量),再加上 \xi ,就构造出了一个与 \xi 非常接近的值,并计算 S({\large\frac{a}{2^k}}+\xi) 的值,

因为 {\large\frac{a}{2^k}} 很小,所以 S({\large\frac{a}{2^k}}+\xi) 近似等于 {\large\frac1{10}}({\large\frac{a}{2^k}}+\xi-\xi)+S(\xi)={\large\frac1{10}}{\large\frac{a}{2^k}}+S(\xi)

于是 (S({\large\frac{a}{2^k}}+\xi)-S(\xi))\times2^{k} 近似等于 {\large\frac a{10}}

好了,现在唯一的问题就是, \xi 等于多少?

解方程 S'(\xi)={\large\frac{e^{-\xi}}{(1+e^{-\xi})^2}}={\large\frac1{10}}

得: 10e^{-\xi}=(1+e^{-\xi})^2

得: e^{-2\xi}-8e^{-\xi}+1=0

使用求根公式得: e^{-\xi}=4\pm\sqrt{15}

钦定 \xi>0 ,所以 0<e^{-\xi}<1 ,所以舍去 4+\sqrt{15} ,得:e^{-\xi}=4-\sqrt{15}

于是 \xi=-\ln(4-\sqrt{15})=\ln(4+\sqrt{15})

于是 S(\xi)={\large\frac1{1+e^{-\xi}}}={\large\frac1{5-\sqrt{15}}}={\large\frac{5+\sqrt{15}}{10}}

好了,现在该如何计算出这两个玩意儿?

因为我们最后会乘个 2^k ,所以这两个玩意儿的精度必须极其高才行。

那么先算 k

于是这个测试点可以过了。

输出代码

```cpp if(n==8) { // 6 分代码,使用乘法节点。 printf("I\n"); printf("> 1 233\n"); // 造零。 printf("C 2 0.1\n"); // 造0.1。 printf("* 1 3\n"); printf("O 4"); } ``` 满分,$7$ 行: ```cpp if(n==8) { // 7 行。 printf("I\n"); //printf("C 2 2.063437068895560546727281172620131871456591449883392499836032692765902842847409911780353006\n"); printf("> 1 178\n"); printf("C 2 2.0634370688955605467272811726201\n"); // 计算器答案。 printf("S 3\n"); //printf("C 4 -0.887298334620741688517926539978239961083292170529159082658757376611348309193697903351928738\n"); printf("C 4 -0.887298334620741688517926539978236773937633025540819832675154107295416657242528255923059519\n"); printf("< 5 178\n"); printf("O 6"); } ``` # $\text{task9}

要求

输入:a_1,\cdots,a_{16}

输入限制:|a_1|,\cdots,|a_{16}|\leqslant10^9 ,小数部分不超过 9 位。

输出:输出 16 个实数,表示 a_1,\cdots,a_{16} 从小到大排序后的结果。

满分行数:3000

解决方法

计算节点太单一了,所以有些操作很难实现。

很多高效的排序方法都不能用了,只能使用冒泡排序、选择排序这些基本的算法。

一步一步来考虑。

输出代码

满分,1392 行:

if(n==9) { // 1392 行。 
    const int N=16; // 数据总数。 
    int pos[17],row=0,a,b; // 16个数据当前的位置,当前输出行数。两个临时节点。 
    for(int i=1;i<=N;++i) printf("I\n"),pos[i]=i,++row;
    for(int i=1,i2=2;i2<=N;++i,i2<<=1) {
        for(int j=1,j2=i2-1;j2>=1;++j,j2-=2) {
            for(int k=j;k<=N;k+=i2) {   // 每次交换 17 行。 
                a=k;b=k+j2;
                ++row;  printf("+ %d %d\n",pos[a],pos[b]);              // u=a+b;
                ++row;  printf("- %d\n",pos[b]);                        // -b;
                ++row;  printf("+ %d %d\n",pos[a],row-1);               // x=a+-b;
                ++row;  printf("C %d 0.0000000001\n",row-1);            // x+eps;
                ++row;  printf("< %d 41\n",row-1);                      // (x+eps)<<41;
                ++row;  printf("S %d\n",row-1);                         // S((x+eps)<<41);
                ++row;  printf("< %d 179\n",row-1);                     // t=S((x+eps)<<41)<<179;
                ++row;  printf("> %d 178\n",row-5);                     // x>>178;
                ++row;  printf("+ %d %d\n",row-1,row-2);                // (x>>178)+t;
                ++row;  printf("S %d\n",row-1);                         // S((x>>178)+t);
                ++row;  printf("C %d -0.5\n",row-1);                    // S(...)-0.5;
                ++row;  printf("< %d 180\n",row-1);                     // (S(...)-0.5)<<180;
                ++row;  printf("- %d\n",row-1);                         // -(S(...)-0.5)<<180;
                ++row;  printf("+ %d %d\n",row-7,row-1);                // -min=t-(S(...)-0.5)<<180;
                ++row;  printf("+ %d %d\n",pos[a],row-1);   pos[b]=row; // p=a-min; b'=p;
                ++row;  printf("- %d\n",row-1);                         // -p;
                ++row;  printf("+ %d %d\n",row-16,row-1);   pos[a]=row; // q=u+-p;  a'=q;
            }
        }
        for(int j=(i2>>2);j>=1;j>>=1) {
            for(int k=1;k<=j;++k) {
                for(int l=k;l<=N;l+=(j<<1)) {
                    a=l;b=l+j;
                    ++row;  printf("+ %d %d\n",pos[a],pos[b]);              // u=a+b;
                    ++row;  printf("- %d\n",pos[b]);                        // -b;
                    ++row;  printf("+ %d %d\n",pos[a],row-1);               // x=a+-b;
                    ++row;  printf("C %d 0.0000000001\n",row-1);            // x+eps;
                    ++row;  printf("< %d 41\n",row-1);                      // (x+eps)<<41;
                    ++row;  printf("S %d\n",row-1);                         // S((x+eps)<<41);
                    ++row;  printf("< %d 179\n",row-1);                     // t=S((x+eps)<<41)<<179;
                    ++row;  printf("> %d 178\n",row-5);                     // x>>178;
                    ++row;  printf("+ %d %d\n",row-1,row-2);                // (x>>178)+t;
                    ++row;  printf("S %d\n",row-1);                         // S((x>>178)+t);
                    ++row;  printf("C %d -0.5\n",row-1);                    // S(...)-0.5;
                    ++row;  printf("< %d 180\n",row-1);                     // (S(...)-0.5)<<180;
                    ++row;  printf("- %d\n",row-1);                         // -(S(...)-0.5)<<180;
                    ++row;  printf("+ %d %d\n",row-7,row-1);                // -min=t-(S(...)-0.5)<<180;
                    ++row;  printf("+ %d %d\n",pos[a],row-1);   pos[b]=row; // p=a-min; b'=p;
                    ++row;  printf("- %d\n",row-1);                         // -p;
                    ++row;  printf("+ %d %d\n",row-16,row-1);   pos[a]=row; // q=u+-p;  a'=q;
                }
            }
        }
    }
    for(int i=1;i<=N;++i) printf("O %d\n",pos[i]);
}

\text{task10}

要求

输入:a,b,m

输入限制:0\leqslant a,b<2^{32}1\leqslant m<2^{32}a,b,m 均为整数。

输出:a\times b 除以 m 的余数。

满分行数:2000 行。

解决方法

首先,这个式子没有化简的余地。

使用 a\times b\mod m=((a\mod m)\times(b\mod m))\mod m 更不行,虽然每次取余所需的计算节点个数少了,但总的取余次数多了。

看来不管怎么搞,我们都避不开乘法操作。

那就先来讲讲怎么实现乘法。

至此,我们用比快速乘更少的节点( 35 个)计算出了 a\times b

下面讲如何完成取余操作。

输出代码

满分,807 行:

if(n==10) { // 807 行。
    const int W=130;
    char ln3[]={"1.098612288668109691395245236922525704647490557822749451734694333637494293218608966873615755"};

    //  1.316957896924816708625046347307968444026981971467516479768472256920460185416443976074219013
    // -0.25
    // =1.066957896924816708625046347307968444026981971467516479768472256920460185416443976074219013
    char xi []={"1.066957896924816708625046347307968444026981971467516479768472256920460185416443976074219013"};

    //  0.78867513459481288225457439025097872782380087563506343800930116324198883615146667 2846857698
    // +0.00000000000000000000000000000000000000000000000000000000000000000000000000000000 0843375836  // 0.1>>266;
    // =0.78867513459481288225457439025097872782380087563506343800930116324198883615146667 3690233534
    char Sxi[]={"0.788675134594812882254574390250978727823800875635063438009301163241988836151466673690233534"};
    printf("I\n");                      
    printf("I\n");
    printf("I\n");
    printf("> 1 %d\n",W);
    printf("> 2 %d\n",W);               // 5。 
    printf("+ 4 5\n");
    printf("C 4 -%s\n",ln3);            
    printf("C 5 -%s\n",ln3);            
    printf("C 6 -%s\n",ln3);            
    printf("S 7\n");                    // 10。 
    printf("S 8\n");
    printf("S 9\n");
    printf("> 4 4\n");                  
    printf("> 4 3\n");
    printf("> 5 4\n");                  // 15。 
    printf("> 5 3\n");
    printf("+ 13 14\n");
    printf("+ 15 16\n");
    printf("- 17\n");
    printf("- 18\n");                   // 20。 
    printf("+ 19 20\n");
    printf("+ 10 19\n");
    printf("+ 11 20\n");
    printf("+ 12 21\n");
    printf("C 22 %s\n",xi);             // 25。 
    printf("C 23 %s\n",xi);
    printf("C 24 %s\n",xi);
    printf("S 25\n");
    printf("S 26\n");
    printf("S 27\n");                   // 30
    printf("+ 28 29\n");
    printf("- 30\n");
    printf("+ 31 32\n");
    printf("C 33 -%s\n",Sxi);
    printf("< 34 %d\n",W*2+6+41);       // 35。 // 提前左移 41 位,加上本来应该左移的 266 位。 
    for(int i=36,j=63;j>=0;--j,i+=12) {
        printf("< 3 %d\n",41+j);        // M=m<<i;
        printf("+ %d %d\n",i,i-1);      // -ab+M;
        printf("S %d\n",i+1);           // p=S((-ab+M)<<41);
        printf("< %d 179\n",i+2);       // t=p<<179;
        printf("> %d 178\n",i);         // M>>178;
        printf("+ %d %d\n",i+4,i+3);
        printf("S %d\n",i+5);
        printf("C %d -0.5\n",i+6);
        printf("< %d %d\n",i+7,180);
        printf("- %d\n",i+3);           // -t;
        printf("+ %d %d\n",i+9,i+8);
        printf("+ %d %d\n",i+10,i-1);   // -ab+M or -ab+0;
    }
    printf("> 803 41\n");
    printf("C 804 0.100000000064\n");   // 因为 0.1>>266 时产生了一些精度误差,所以在这个地方往回补一补精度。 
    printf("- 805\n");
    printf("O 806");
}

结语