(mathcal{Description})
Link.
定义一个运算结点 (u) 有两个属性:当前容量 (x_u)、最大容量 (V_u)。提供以下单元操作:
-
I
读入一个整数 (x),令新结点 (u=(x,x))。 -
F u
装满 (u) 结点,即令 (x_u=V_u)。 -
E u
清空 (u) 结点,即令 (x_u=0)。 -
C s
令新结点 (u=(0,s))。 -
M u
令新结点 (v=(0,x_u))。 -
T u v
不断令 (x_uleftarrow x_u-1,x_vleftarrow x_v-1) 直到 (x_u=0) 或 (x_v=V_v)。
构造不超过 (10^4) 次操作的一个运算方法,输入 (a,b),输出 (abmod 2^{18})。
(0le a,ble 10^5)。
(mathcal{Solution})
貌似是瓶子国的某个点?反正很离谱就是了。
首先观察操作,我们只有一个二元运算 (T(u,v)),必然只能用它实现逻辑模块。
再从问题入手,不难想到可以龟速乘计算 (ab),过程中若值 (ge 2^{18}),则减去 (2^{18})。那么我们至少需要实现这些模块:
-
加法器:输入结点 (u,v),输出 (w),满足 (x_w=x_u+x_v)。
-
逻辑减法器:输入结点 (u,v),输出 (w),若 (x_uge x_v),(x_w=x_u-x_v);否则 (x_w=x_u)。
加法器比较方便:新建一个大容量点,把两个加数复制一份倒进去就好。先实现一个复制当前容量器:
inline int copyNum( const int u ) {
printf( "M %d
", u ), ++node;
printf( "F %d
", node );
return node;
}
再实现加法器:
inline int add( const int u, const int v ) {
printf( "C 1000000000
" ); int res = ++node;
printf( "T %d %d
", copyNum( u ), res );
printf( "T %d %d
", copyNum( v ), res );
return res;
}
逻辑减法?先要判断大小关系。而 (T(u,v)) 之后 (x'_u=max{x_u-x_v,0}),我们只需要判断一个结点的当前容量是否为 (0)。好消息是,我们能够实现逻辑非器:
inline int logicNot( const int u ) {
printf( "M %d
", u ), ++node;
puts( "C 1" ), ++node;
printf( "F %d
", node );
printf( "T %d %d
", node, node - 1 );
return node;
}
内部逻辑比较易懂就不讲啦。在此基础上,实现普通减法器和逻辑减法器:
inline int sub( const int u, const int v ) {
printf( "M %d
", v ); int tmp = ++node;
printf( "T %d %d
", copyNum( u ), tmp );
return node;
}
inline PII logicSub( int u, int v ) {
int f = logicNot( sub( u = add( u, 1 ), v ) );
rep ( i, 1, 18 ) f = add( f, f );
printf( "M %d
", f ), f = ++node;
printf( "T %d %d
", v = copyNum( v ), f );
return { u = sub( sub( u, v ), 1 ), f };
}
逻辑减法器返回的 first
即减法结果,second
用于求解时重复利用。
底层方法实现之后,剩下的工作就简单了:输入 (a,b),计算 (2a,4a,cdots,2^{16}a),枚举 (b) 是否大于等于 (2^{16}dots2^0),若是,则减去,答案加上对应的权。都能用以逻辑非为基础的模块实现。
操作次数复杂度为 (mathcal O(log^2 V))(倍增以及内部的逻辑减法),我实现的常数较大,不过封装成模块很易懂就是了。(
(mathcal{Code})
/*+Rainybunny+*/
#include <bits/stdc++.h>
#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
typedef std::pair<int, int> PII;
#define fi first
#define se second
int node, mod;
namespace StandardModuleLibrary {
inline int logicNot( const int u ) {
printf( "M %d
", u ), ++node;
puts( "C 1" ), ++node;
printf( "F %d
", node );
printf( "T %d %d
", node, node - 1 );
return node;
}
inline int copyNum( const int u ) {
printf( "M %d
", u ), ++node;
printf( "F %d
", node );
return node;
}
inline int add( const int u, const int v ) {
printf( "C 1000000000
" ); int res = ++node;
printf( "T %d %d
", copyNum( u ), res );
printf( "T %d %d
", copyNum( v ), res );
return res;
}
inline int sub( const int u, const int v ) {
printf( "M %d
", v ); int tmp = ++node;
printf( "T %d %d
", copyNum( u ), tmp );
return node;
}
inline PII logicSub( int u, int v ) {
int f = logicNot( sub( u = add( u, 1 ), v ) );
rep ( i, 1, 18 ) f = add( f, f );
printf( "M %d
", f ), f = ++node;
printf( "T %d %d
", v = copyNum( v ), f );
return { u = sub( sub( u, v ), 1 ), f };
}
} using namespace StandardModuleLibrary;
int main() {
freopen( "liver.out", "w", stdout );
int x, y;
printf( "C 1
F 1
" ), node = 1; // element 1.
printf( "C 262144
F 2
" ), mod = node = 2; // module.
puts( "I" ), x = ++node;
puts( "I" ), y = ++node;
int buc[30] = { x }, pwr[30] = {};
int ans = ++node; puts( "C 1000000000" );
pwr[0] = ++node, printf( "C 1
F %d
", node );
rep ( i, 1, 16 ) {
pwr[i] = ++node, printf( "C %d
F %d
", 1 << i, node );
PII shit( logicSub( add( buc[i - 1], buc[i - 1] ), mod ) );
buc[i] = shit.fi;
}
per ( i, 16, 0 ) {
PII r( logicSub( y, pwr[i] ) );
int x = r.se;
rep ( j, 1, 18 - i ) x = add( x, x );
printf( "M %d
", x ), x = ++node;
printf( "T %d %d
", buc[i], x ), y = r.fi;
ans = logicSub( add( ans, buc[i] ), mod ).fi;
}
printf( "P %d
", ans );
return 0;
}