Solution -「OurOJ 46544」漏斗计算
Link.
定义一个运算结点 有两个属性:当前容量 、最大容量 。提供以下单元操作:
-
I
读入一个整数 ,令新结点 。 -
F u
装满 结点,即令 。 -
E u
清空 结点,即令 。 -
C s
令新结点 。 -
M u
令新结点 。 -
T u v
不断令 直到 或 。
构造不超过 次操作的一个运算方法,输入 ,输出 。
。
貌似是瓶子国的某个点?反正很离谱就是了。
首先观察操作,我们只有一个二元运算 ,必然只能用它实现逻辑模块。
再从问题入手,不难想到可以龟速乘计算 ,过程中若值 ,则减去 。那么我们至少需要实现这些模块:
-
加法器:输入结点 ,输出 ,满足 。
-
逻辑减法器:输入结点 ,输出 ,若 ,;否则 。
加法器比较方便:新建一个大容量点,把两个加数复制一份倒进去就好。先实现一个复制当前容量器:
inline int copyNum( const int u ) {
printf( "M %d\n", u ), ++node;
printf( "F %d\n", node );
return node;
}
再实现加法器:
inline int add( const int u, const int v ) {
printf( "C 1000000000\n" ); int res = ++node;
printf( "T %d %d\n", copyNum( u ), res );
printf( "T %d %d\n", copyNum( v ), res );
return res;
}
逻辑减法?先要判断大小关系。而 之后 ,我们只需要判断一个结点的当前容量是否为 。好消息是,我们能够实现逻辑非器:
inline int logicNot( const int u ) {
printf( "M %d\n", u ), ++node;
puts( "C 1" ), ++node;
printf( "F %d\n", node );
printf( "T %d %d\n", node, node - 1 );
return node;
}
内部逻辑比较易懂就不讲啦。在此基础上,实现普通减法器和逻辑减法器:
inline int sub( const int u, const int v ) {
printf( "M %d\n", v ); int tmp = ++node;
printf( "T %d %d\n", 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\n", f ), f = ++node;
printf( "T %d %d\n", v = copyNum( v ), f );
return { u = sub( sub( u, v ), 1 ), f };
}
逻辑减法器返回的 first
即减法结果,second
用于求解时重复利用。
底层方法实现之后,剩下的工作就简单了:输入 ,计算 ,枚举 是否大于等于 ,若是,则减去,答案加上对应的权。都能用以逻辑非为基础的模块实现。
操作次数复杂度为 (倍增以及内部的逻辑减法),我实现的常数较大,不过封装成模块很易懂就是了。(
/*+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\n", u ), ++node;
puts( "C 1" ), ++node;
printf( "F %d\n", node );
printf( "T %d %d\n", node, node - 1 );
return node;
}
inline int copyNum( const int u ) {
printf( "M %d\n", u ), ++node;
printf( "F %d\n", node );
return node;
}
inline int add( const int u, const int v ) {
printf( "C 1000000000\n" ); int res = ++node;
printf( "T %d %d\n", copyNum( u ), res );
printf( "T %d %d\n", copyNum( v ), res );
return res;
}
inline int sub( const int u, const int v ) {
printf( "M %d\n", v ); int tmp = ++node;
printf( "T %d %d\n", 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\n", f ), f = ++node;
printf( "T %d %d\n", 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\nF 1\n" ), node = 1; // element 1.
printf( "C 262144\nF 2\n" ), 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\nF %d\n", node );
rep ( i, 1, 16 ) {
pwr[i] = ++node, printf( "C %d\nF %d\n", 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\n", x ), x = ++node;
printf( "T %d %d\n", buc[i], x ), y = r.fi;
ans = logicSub( add( ans, buc[i] ), mod ).fi;
}
printf( "P %d\n", ans );
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2020-10-20 Solution -「LGR-087」「洛谷 P6860」象棋与马