luoguP2838
前言
记得这题是洛谷首页给我推荐的题。怪得很,我并没做过任何提交答案题,结果它给我推荐了一发,还是黑的。
好在有空余时间钻研,终于是把它搞出来了。
题解
这是一道xie题,让你写一个程序打印出另一个程序的源码,然后测试你程序打印出的程序能否完成任务。
完成什么任务呢?算
a
+
b
a+b
a+b、
∣
a
−
b
∣
|a-b|
∣a−b∣、
gcd
\gcd
gcd、
a
∗
b
a*b
a∗b之类,总之是一些对先进计算机语言来说很基础的运算。听起来挺简单,但是这题你只能用七种语句,除去输入输出只有五种有用的语句(题面说得很清楚我就不再列举了)。(当然如果你不想 AC
这道题,也可以用第八种语句)
而且这些语句有着令人头疼的弱点:只有顺序结构👍。
这意味着如果代码长了(要扣分),即使不会T
,你也要用算法把他优化得短些,这就需要用到倍增等技巧。而且你需要想方设法实现选择结构!
这些运算虽然基础,但是除了前四个子任务,后面的任务都需要 i f \rm if if 语句 判断。所以这题的难点就在于此。只要实现了高效的 i f \rm if if 语句,所有的问题都好解决了。
怎么实现 i f \rm if if 语句呢?回忆图灵机的原理,我们只需要实现根据某个瓶子水量是否为零来选择执行某个操作,就能实现其它各种各样的判断。我们可以叫它元判断(瞎JB乱扯的名字)。
我们知道,只要一个瓶子的水量为零,那么水量翻倍后还是为零。如果水量不为零,翻倍后会变成一个更大的不为零的数。所以我们还是用倍增,把瓶子里的水量不断翻倍。即使水量只有1,翻倍二十几次也能变成一个极大值。然后我们把翻倍后的水量copy成一个空瓶子 (题目中瓶子容积可以为零,谢天谢地),利用这个空瓶子来转运水,要么可以全部转运,要么一滴水也转运不了,也就是转运失败。这就实现了最基础的根据水量是否为零执行倒水操作😀。
然后就剩些细节了:
- task 5:每次需要判断 a a a 与 2 i 2^i 2i 的大小关系,如果 a ≥ 2 i a≥2^i a≥2i 就要减去 2 i 2^i 2i ,可以搞一个水量 a + 1 a+1 a+1 的瓶子,再向容量为 2 i 2^i 2i 的空瓶倒水,判断剩余水量是否大于0。同理,可以把剩余水倒入一个大小为1的瓶子(相当于整型转布尔型),这个瓶子的水量就是这个二进制位上的值。
- task 6:倍增乘法。每次判断 a a a 减去2的某次幂后是否 ≥ 0 ≥0 ≥0,所以还是得先加1转化为判断大于0。
- task 7:异或就是不进位的加法。好好运用来之不易的 i f \rm if if 语句,来实现模运算吧。
- task 8:整除操作和取模操作非常相似,只需要在倍增减去除数的同时记录减了多少即可。
- task 9:乘法中取模,注意此处取模不要倍增,只减一次就行。
- task 10:注意在倍增乘法执行失败时乘的是1不是0。
- 全局大坑:新建或copy后的瓶子是空的,而copy的又是水量不是容积,所以记得不要少了Fill操作。
代码
对不起,这是道提答题。我对每个子任务单独写了不超过200行的生成代码的代码,所以码量太大搬不上来。
但是,我是有码人士,所以我会给一个我写的乘法操作以供参考。
inline int mul(int a,int b,int ti){//ti是倍增次数,调节用的
printf("C 233333333\nC 233333333\n"),d+=2;
int p=d-1,q=d;
printf("C 1\nM %d\n",b),d+=2;
f[0]=d-1,g[0]=d;
for(int i=1;i<=ti;i++){
printf("C %d\n",1<<i),d++;
f[i]=d;
printf("F %d\nT %d %d\n",g[i-1],g[i-1],p);
printf("F %d\nT %d %d\n",g[i-1],g[i-1],p);
printf("M %d\n",p),d++;
g[i]=d;
printf("E %d\n",p);
}
printf("F %d\nT %d %d\nT %d %d\nE %d\n",f[0],f[0],p,a,p,f[0]);
printf("M %d\nE %d\nF %d\n",p,p,d+1),d++,a=d;
for(int i=ti;i>=0;i--){
printf("M %d\nF %d\n",a,d+1),d++;
printf("T %d %d\n",d,f[i]);
printf("M %d\n",d),d++;
int c=d;
printf("F %d\nT %d %d\n",c,c,p);
for(int j=1;j<=21;j++){//倍增if
printf("M %d\nF %d\n",p,d+1),d++;
printf("T %d %d\n",d,p);
}
printf("M %d\nE %d\n",p,p),d++,c=d;
printf("F %d\nT %d %d\n",g[i],g[i],c);
printf("T %d %d\n",c,q);
printf("F %d\nT %d %d\n",f[i],f[i],c);
printf("M %d\n",c),d++;
printf("T %d %d\n",a,d);
}
return q;
}