给你两种操作: a k ← a i + a j a_k\gets a_i+a_j ak←ai+aj, a k ← [ a i < a j ] a_k\gets [a_i
sol:思维题。首先我们要知道目的是用上述两种简单运算构造出比较复杂度情况,这里有一个引理:
1.1 1.1 1.1 若 A , B ∈ { 0 , 1 } A,B\in \{0,1\} A,B∈{0,1},那么 A × B = [ 1 < A + B ] A\times B=[1A×B=[1<A+B]
这条引理告诉我们如何构造最为基本的乘法运算。
让我们想想。什么样的数只有 0 / 1 0/1 0/1 ?显然 数的二进制表示。
考虑一般情况。设 A = ( a n a n − 1 . . . a 0 ) 2 A=(a_na_{n-1}...a_0)_2 A=(anan−1...a0)2, B = ( b m b m − 1 . . . b 0 ) 2 B=(b_mb_{m-1}...b_0)_2 B=(bmbm−1...b0)2,那么 A × B = ∑ i = 0 n ∑ j = 0 m ( a i b j ) i + j A\times B=\sum_{i=0}^n\sum_{j=0}^m(a_ib_j)^{i+j} A×B=∑i=0n∑j=0m(aibj)i+j 。而我们知道 x 2 i x2^i x2i可以通过将 x x x自我复制 i i i次得到,因此我们只需将 A A A, B B B二进制拆分即可。
如何拆分?我们从高到低位考虑,最高位等价于 [ A ≥ 2 k ] [A\ge 2^k] [A≥2k],对于低位只需抵消高位的影响即可。(在式子右边加上之前分出来的高位)以此类推,可以递推的把每一位求出来。
总序列长度 O ( log 3 n ) O(\log^3 n) O(log3n)。
#include
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
int A,B,tot=4;
struct node{
int op,a,b,c;
};
vector<node>v;
void add(int x,int y,int z){v.pb({0,y,z,x});}
void get(int x,int y,int z){v.pb({1,y,z,x});}
int mul(int x,int y){
add(++tot,x,y),get(tot+1,3,tot),tot++;
return tot;
}
int fac(int x,int y){
if(y==0)return x;
add(++tot,x,x),y--;
for(int i=0;i<y;i++)add(tot+1,tot,tot),tot++;
return tot;
}
vector<int>div(int A){
vector<int>v;int lst=++tot;//之前的和
add(++tot,A,3);int l=tot;
for(int i=29;i>=0;i--){
int x=fac(3,i);add(++tot,x,lst);get(tot+1,tot,l),tot++;v.pb(tot);
add(++tot,lst,fac(tot,i)),lst=tot;
}reverse(v.begin(),v.end());return v;
}
int main(){
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(false);add(4,0,1),get(3,3,4);
vector<int>v1=div(0),v2=div(1);
for(int i=0;i<v1.size();i++){
for(int j=0;j<v2.size();j++){
add(2,2,fac(mul(v1[i],v2[j]),i+j));
}
}
cout<<v.size()<<"\n";
for(auto x:v){
cout<<(x.op?'<':'+')<<' '<<x.a<<' '<<x.b<<' '<<x.c<<"\n";
}
}