题目链接
这道题实在是毒瘤。以及洛谷上的翻译其实是我翻的,质量跟机翻差不多。
首先我们确定求出 x y xy xy 的整体思路—— x y = ( x + y ) 2 − x 2 − y 2 xy=(x+y)^2−x^2−y^2 xy=(x+y)2−x2−y2。
这个思路不难想到:我们能够比较容易实现的两个操作数的运算只有加、减,于是我们将运算转换为加、减与只有一个操作数的平方、除以 2。关键在于如何实现这些运算。
前置技能
- 快速幂/乘
- 高斯消元
- exgcd 求乘法逆元
- 少量数学知识
- 大量耐心
我们先考虑几个简单的运算:
0
在求解过程中,有一个数字 0 是很方便的,利用 ·+ zero zero x· 就可以清空一格。
在这里需要用到快(慢?)速乘,实现:
int f=p-1;
while(f){
if(f&1ll){
cout<<"+ "<<4998<<" "<<zero<<" "<<zero<<endl;
}
cout<<"+ "<<4998<<" "<<4998<<" "<<4998<<endl;
f/=2;
}
//zero=4999
这样 zero
格就有了一个常数 0。
乘以常数
同样是快速乘。要注意的是,每次必须清空所需格子。
int getmulti(int a,int b,int q){
b=(b%p+p)%p;
int ans=(q?q:++tot);
int x=++tot;
while(b){
if(b&1ll){
cout<<"+ "<<x<<" "<<ans<<" "<<ans<<endl;
}
cout<<"+ "<<x<<" "<<x<<" "<<x<<endl;
b/=2;
}
return ans;
}
//如果有 q ,答案存在 q 当中。返回结果所在格子。
减法
a − b = a + ( p − 1 ) b a−b=a+(p−1)b a−b=a+(p−1)b
int jian(int a,int b){
b=getmulti(b,p-1);
++tot;
cout<<"+ "<<a<<" "<<b<<" "<<tot<<endl;
return tot;
}
除以常数
即乘以其逆元。
下面是重头戏:
求一个数的平方
很明显, x 2 x^2 x2 并不能通过 x d x^d xd 直接求出。
我们考虑将 x 2 x^2 x2 表达为 a 0 ( x + 0 ) d + a 1 ( x + 1 ) + ⋯ + a d ( x + d ) d a_0(x+0)^d+a_1(x+1)^+\dots+a_d(x+d)^d a0(x+0)d+a1(x+1)+⋯+ad(x+d)d
将它展开,再按 x x x 合并同类项,得:
x 2 = C d 0 ( a 0 + a 1 + ⋯ + a d ) x d + C d 1 (