谨以此文献给所有在 A+B 问题上"过度设计"的先驱们
在无数 OJ 的角落里,有一道题,它叫 P1001 A+B Problem。
它只有一行需求:输入两个数,输出它们的和。
勇士们拿起算法的武器,向一座看似坚固的"简单"堡垒发起了冲锋。
这是一部人类群星闪耀史。
有人说,程序是冰冷的。LCT 哥不同意。
他建了两个节点,让它们相识——connect(A, B)。
然后分手——cut(A, B)。
最后复合——connect(A, B)。
在经历了完整的情感波折后,他查询了它们的"爱情结晶"——路径和。
两百行代码,只为一个浪漫的加法。
"动态树:我用 200 行代码,证明了我能算 1+1。"
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
int data,rev,sum;
node *son[2],*pre;
bool judge();
bool isroot();
void pushdown();
void update();
void setson(node *child,int lr);
}lct[233];
int top,a,b;
node *getnew(int x)
{
node *now=lct+ ++top;
now->data=x;
now->pre=now->son[1]=now->son[0]=lct;
now->sum=0;
now->rev=0;
return now;
}
bool node::judge(){return pre->son[1]==this;}
bool node::isroot()
{
if(pre==lct)return true;
return !(pre->son[1]==this||pre->son[0]==this);
}
void node::pushdown()
{
if(this==lct||!rev)return;
swap(son[0],son[1]);
son[0]->rev^=1;
son[1]->rev^=1;
rev=0;
}
void node::update(){sum=son[1]->sum+son[0]->sum+data;}
void node::setson(node *child,int lr)
{
this->pushdown();
child->pre=this;
son[lr]=child;
this->update();
}
void rotate(node *now)
{
node *father=now->pre,*grandfa=father->pre;
if(!father->isroot()) grandfa->pushdown();
father->pushdown();now->pushdown();
int lr=now->judge();
father->setson(now->son[lr^1],lr);
if(father->isroot()) now->pre=grandfa;
else grandfa->setson(now,father->judge());
now->setson(father,lr^1);
father->update();now->update();
if(grandfa!=lct) grandfa->update();
}
void splay(node *now)
{
if(now->isroot())return;
for(;!now->isroot();rotate(now))
if(!now->pre->isroot())
now->judge()==now->pre->judge()?rotate(now->pre):rotate(now);
}
node *access(node *now)
{
node *last=lct;
for(;now!=lct;last=now,now=now->pre)
{
splay(now);
now->setson(last,1);
}
return last;
}
void changeroot(node *now)
{
access(now)->rev^=1;
splay(now);
}
void connect(node *x,node *y)
{
changeroot(x);
x->pre=y;
access(x);
}
void cut(node *x,node *y)
{
changeroot(x);
access(y);
splay(x);
x->pushdown();
x->son[1]=y->pre=lct;
x->update();
}
int query(node *x,node *y)
{
changeroot(x);
node *now=access(y);
return now->sum;
}
int main()
{
scanf("%d%d",&a,&b);
node *A=getnew(a);
node *B=getnew(b);
connect(A,B);
cut(A,B);
connect(A,B);
printf("%d\n",query(A,B));
return 0;
}线段树哥说:"我的代码要有工业级的冗余。"
于是他开了一棵 4×1e5 的线段树,建在只有 1 个元素的数组上。
他用标记永久化做区间加,虽然只加了一次。
他传了 tags 参数累加路径标记,虽然路径只有一层。
50 倍的常数,只为 100 分的尊严。
"用集装箱存一颗芝麻,然后骄傲地说:看,我的芝麻有专属仓库!"
#include <cstdio>
#define mid L + (R-L >> 1)
const int maxn = 1e5+5;
int n, a[maxn], m;
int sl, sr, add;
struct segtree{
int sum[maxn<<2], tag[maxn<<2];
inline int lc(int o){return o<<1;}
inline int rc(int o){return o<<1|1;}
void build(int o, int L, int R){
if(L == R){sum[o] = a[L];return;}
int M = mid;
build(lc(o), L, M);
build(rc(o), M+1, R);
sum[o] = sum[lc(o)] + sum[rc(o)];
}
void maintain(int o, int L, int R){
if(R>L){
sum[o] = sum[lc(o)] + sum[rc(o)];
sum[o] += tag[o] * (R-L+1);
} else {
sum[o] += tag[o];
tag[o] = 0;
}
}
void updata(int o, int L, int R){
if(sl <= L && R <= sr)tag[o] += add;
else{
int M = mid;
if(sl <= M)updata(lc(o), L, M);
if(sr > M)updata(rc(o), M+1, R);
}
maintain(o, L, R);
}
int query(int o, int L, int R, int tags){
if(sl <= L && R <= sr)return sum[o] + tags * (R-L+1);
else {
int M = mid, res = 0;
if(sl <= M)res += query(lc(o), L, M, tags+tag[o]);
if(sr > M)res += query(rc(o), M+1, R, tags+tag[o]);
return res;
}
}
} sol;
signed main(){
n = 1;
int a, b;
scanf("%d%d", &a, &b);
sol.build(1, 1, n);
add=a; sl=1; sr=1;
sol.updata(1, 1, 1);
add=b;
sol.updata(1, 1, 1);
printf("%d\n", sol.query(1, 1, n, 0));
return 0;
}树状数组哥更狠。
他开了 500005 个空间,用 lowbit 维护 1 个数的前缀和。
只有 1 个数,但数组要开到 50 万。
空着 499999 个格子,是一种态度。
"邻居问:你家储物柜为什么空着 499999 格?"
#include <iostream>
using namespace std;
const int n = 1;
int a, b;
int c[500005];
inline int lowbit(int x){
return x & (-x);
}
inline int sum(int x){
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=c[i];
return ans;
}
void add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=y;
}
int main(){
cin>>a>>b;
add(1, a); add(1, b);
printf("%d\n", sum(1));
return 0;
}Splay 哥说:"加法满足交换律。"
然后他用 100 行代码实现了 Splay,建了 4 个节点。
把序列 [a, b] 翻转成 [b, a]。
最后查询区间和——a+b 还是 a+b。
他证明了:翻转不影响结果,正如爱不影响加法。
"翻转序列是为了证明加法交换律,数学老师哭了。"
#include <bits/stdc++.h>
#define ll long long
#define N 100000
using namespace std;
int sz[N], rev[N], tag[N], sum[N], ch[N][2], fa[N], val[N];
int n, m, rt, x;
void push_up(int x){
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
sum[x] = sum[ch[x][1]] + sum[ch[x][0]] + val[x];
}
void push_down(int x){
if(rev[x]){
swap(ch[x][0], ch[x][1]);
if(ch[x][1]) rev[ch[x][1]] ^= 1;
if(ch[x][0]) rev[ch[x][0]] ^= 1;
rev[x] = 0;
}
if(tag[x]){
if(ch[x][1]) tag[ch[x][1]] += tag[x], sum[ch[x][1]] += tag[x];
if(ch[x][0]) tag[ch[x][0]] += tag[x], sum[ch[x][0]] += tag[x];
tag[x] = 0;
}
}
void rotate(int x, int &k){
int y = fa[x], z = fa[fa[x]];
int kind = ch[y][1] == x;
if(y == k) k = x;
else ch[z][ch[z][1]==y] = x;
fa[x] = z; fa[y] = x; fa[ch[x][!kind]] = y;
ch[y][kind] = ch[x][!kind]; ch[x][!kind] = y;
push_up(y); push_up(x);
}
void splay(int x, int &k){
while(x != k){
int y = fa[x], z = fa[fa[x]];
if(y != k) if(ch[y][1] == x ^ ch[z][1] == y) rotate(x, k);
else rotate(y, k);
rotate(x, k);
}
}
int kth(int x, int k){
push_down(x);
int r = sz[ch[x][0]]+1;
if(k == r) return x;
if(k < r) return kth(ch[x][0], k);
else return kth(ch[x][1], k-r);
}
void split(int l, int r){
int x = kth(rt, l), y = kth(rt, r+2);
splay(x, rt); splay(y, ch[rt][1]);
}
void rever(int l, int r){
split(l, r);
rev[ch[ch[rt][1]][0]] ^= 1;
}
void add(int l, int r, int v){
split(l, r);
tag[ch[ch[rt][1]][0]] += v;
val[ch[ch[rt][1]][0]] += v;
push_up(ch[ch[rt][1]][0]);
}
int build(int l, int r, int f){
if(l > r) return 0;
if(l == r){
fa[l] = f;
sz[l] = 1;
return l;
}
int mid = l + r >> 1;
ch[mid][0] = build(l, mid-1, mid);
ch[mid][1] = build(mid+1, r, mid);
fa[mid] = f;
push_up(mid);
return mid;
}
int asksum(int l, int r){
split(l, r);
return sum[ch[ch[rt][1]][0]];
}
int main(){
n = 2;
rt = build(1, n+2, 0);
for(int i = 1; i <= n; i++){
scanf("%d", &x);
add(i, i, x);
}
rever(1, n);
printf("%d\n", asksum(1, n));
return 0;
}Treap 哥说:"插入 a 个 1,插入 b 个 1,然后查 1 的排名。"
于是循环跑了 a+b 次,插了 200 万个 1。
树高 20,旋转 4000 万次,CPU 风扇狂转。
最后输出 2000000。
"算个加法用了 4000 万次旋转,隔壁做渲染的以为我在跑 3D 建模。"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
inline int read(){int a = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9'){a = a * 10 + ch - '0'; ch = getchar(); }
return a * f;}
inline void write(int x){if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10); putchar(x % 10 + '0'); }
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int n;
struct Treap{
int ls, rs;
int val, rnd;
int siz, cnt;
}tr[N];
int tot = 0;
inline void Updata(int x){ tr[x].siz = tr[tr[x].ls].siz
+ tr[tr[x].rs].siz + tr[x].cnt; }
inline int New(int x){
tr[++tot].val = x;
tr[tot].rnd = rand();
tr[tot].siz = tr[tot].cnt = 1;
return tot;
}
int root = 0;
inline void Build(){
New(-INF), New(INF);
root = 1;
tr[root].rs = 2;
Updata(root);
}
inline void zig(int &y){
int x = tr[y].ls;
tr[y].ls = tr[x].rs;
tr[x].rs = y;
y = x;
Updata(tr[y].rs);
Updata(y);
}
inline void zag(int &y){
int x = tr[y].rs;
tr[y].rs = tr[x].ls;
tr[x].ls = y;
y = x;
Updata(tr[y].ls);
Updata(y);
}
inline void Insert(int &x, int val){
if(!x){
x = New(val);
return ;
}
if(tr[x].val == val){
++tr[x].cnt;
Updata(x);
return ;
}
if(val < tr[x].val){
Insert(tr[x].ls, val);
if(tr[x].rnd < tr[tr[x].ls].rnd) zig(x);
}
else{
Insert(tr[x].rs, val);
if(tr[x].rnd < tr[tr[x].rs].rnd) zag(x);
}
Updata(x);
}
inline void Delete(int &x, int val){
if(!x) return ;
if(tr[x].val == val){
if(tr[x].cnt > 1){
--tr[x].cnt;
Updata(x);
return ;
}
if(!tr[x].ls && !tr[x].rs){
x = 0;
return ;
}
else if(!tr[x].rs || (tr[x].ls && tr[tr[x].ls].rnd > tr[tr[x].rs].rnd)){
zig(x);
Delete(tr[x].rs, val);
}
else{
zag(x);
Delete(tr[x].ls, val);
}
}
else if(val < tr[x].val) Delete(tr[x].ls, val);
else Delete(tr[x].rs, val);
Updata(x);
}
inline int get_rank(int x, int val){
if(!x) return 0;
if(tr[x].val == val) return tr[tr[x].ls].siz + tr[x].cnt;
if(val < tr[x].val) return get_rank(tr[x].ls, val);
return get_rank(tr[x].rs, val) + tr[tr[x].ls].siz + tr[x].cnt;
}
inline int get_val(int x, int rank){
if(!x) return INF;
if(tr[tr[x].ls].siz >= rank) return get_val(tr[x].ls, rank);
if(tr[tr[x].ls].siz + tr[x].cnt >= rank) return tr[x].val;
return get_val(tr[x].rs, rank - tr[tr[x].ls].siz - tr[x].cnt);
}
inline int get_pre(int x, int val){
int ans = 1;
while(x){
if(val == tr[x].val){
if(tr[x].ls > 0){
x = tr[x].ls;
while(tr[x].rs > 0) x = tr[x].rs;
ans = x;
}
break;
}
if(val > tr[x].val && tr[x].val > tr[ans].val) ans = x;
x = val < tr[x].val ? tr[x].ls : tr[x].rs;
}
return tr[ans].val;
}
inline int get_last(int x, int val){
int ans = 2;
while(x){
if(val == tr[x].val){
if(tr[x].rs > 0){
x = tr[x].rs;
while(tr[x].ls > 0) x = tr[x].ls;
ans = x;
}
break;
}
if(val < tr[x].val && tr[x].val < tr[ans].val) ans = x;
x = val < tr[x].val ? tr[x].ls : tr[x].rs;
}
return tr[ans].val;
}
int main(){
srand(time(0));
int a = read(), b = read();
for(int i = 1; i <= a; i++) {
Insert(root, 1);
}
for(int i = 1; i <= b; i++) {
Insert(root, 1);
}
write(get_rank(root, 1));
return 0;
}Dijkstra 哥建了一张图:0→1(边权 a),1→2(边权 b)。
然后跑了一遍最短路算法:松弛、入队、出队、再松弛。
最后郑重宣布:从 0 到 2 的最短路径是 a+b。
导航软件听了一下自己的光荣事迹,默默把自己卸载了。
"导航软件:我这就下岗?"
#include<bits/stdc++.h>
#define int unsigned long long
#define code using
#define by namespace
#define XWC std;
#define f first
code by XWC
const int N=1e5+10;
struct edge{
int v,w;
};
struct node{
int dis,u;
bool operator>(const node& a) const {return dis>a.dis;}
};
vector<edge> e[N];
int dis[N],vis[N];
int u,v,w;
int qq[10];
priority_queue<node,vector<node>,greater<node> > q;
void Dijkstra(int n,int s){
for(int i=0;i<=n;i++) dis[i]=8e18+10;
for(int i=0;i<=n;i++) vis[i]=0;
dis[s]=0;
while(!q.empty()) q.pop();
q.push({0,s});
while(!q.empty()){
int u=q.top().u;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(auto ed:e[u]){
int v=ed.v,w=ed.w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push({dis[v],v});
}
}
}
}
int n=2,m=1,a,b;
signed main(){
n=2,m=1;
cin>>a>>b;
e[n/2].push_back({n,a+b});
Dijkstra(n,1);
cout<<dis[n];
return 0;
}Kruskal 哥更离谱。
他建了三条边:a、b、INF。
然后跑了最小生成树——选了 a 和 b,INF 被冷落。
那条 INF 边,从出生就注定不被选择。
"边:我不要面子的吗?"
#include <cstdio>
#include <algorithm>
#define INF 2140000000
using namespace std;
struct tree{int x,y,t;}a[10];
bool cmp(const tree&a,const tree&b){return a.t<b.t;}
int f[11],i,j,k,n,m,x,y,t,ans;
int root(int x){if (f[x]==x) return x;f[x]=root(f[x]);return f[x];}
int main(){
for (i=1;i<=10;i++) f[i]=i;
for (i=1;i<=2;i++){
scanf("%d",&a[i].t);
a[i].x=i+1;a[i].y=1;k++;
}
a[++k].x=1;a[k].y=3,a[k].t=INF;
sort(a+1,a+1+k,cmp);
for (i=1;i<=k;i++){
x=root(a[i].x);y=root(a[i].y);
if (x!=y) f[x]=y,ans+=a[i].t;
}
printf("%d\n",ans);
}完全平方哥说:"我们知道 (a+b)² = a² + b² + 2ab。"
所以他写了 150 行高精度:加法、减法、乘法、除法、开根号、比较大小。
然后算 1²+2²+2×1×2=9,开根号得 3。
数学老师看了直摇头:"公式不是这么用的。"
"少年,公式是让你算展开,不是让你绕路的。"
#include <bits/stdc++.h>
using namespace std;
string qf (string a)
{
if (a[0] == '-') return a.substr (1 , a.size () - 1);
return "-" + a;
}
string operator - (string a) {return qf (a);}
string gjc (string a , string b)
{
if (a[0] == '-' && b[0] == '-') return gjc (- a , - b);
else if (a[0] == '-') return - gjc (- a , b);
else if (b[0] == '-') return - gjc (a , - b);
int ans[a.size () + b.size ()] = {};
reverse (a.begin () , a.end ());
reverse (b.begin () , b.end ());
for (int i = 0;i < a.size ();i ++)
for (int j = 0;j < b.size ();j ++)
ans[i + j] += (a[i] - '0') * (b[j] - '0');
for (int i = 0;i < a.size () + b.size ();i ++)
if (ans[i] > 9)
{
int x = ans[i] % 10 , y = ans[i] / 10;
ans[i] = x;
ans[i + 1] += y;
}
string ans2 = "";
for (int i = 1;i <= a.size () + b.size ();i ++)
ans2 += (char) (ans[i - 1] + '0');
if (ans2[ans2.size () - 1] == '0')
ans2 = ans2.substr (0 , ans2.size () - 1);
reverse (ans2.begin () , ans2.end ());
return ans2;
}
string max (string a , string b)
{
if (a[0] == '-' && b[0] == '-')
{
if (- max (- a , - b) == a) return b;
return a;
}
else if (a[0] == '-') return b;
else if (b[0] == '-') return a;
else if (a.size () > b.size ()) return a;
else if (a.size () < b.size ()) return b;
else
{
for (int i = 0;i < a.size ();i ++)
if (a[i] > b[i]) return a;
else if (a[i] < b[i]) return b;
return a;
}
}
string min (string a , string b)
{
if (max (a , b) == a) return b;
return a;
}
string gjj (string a , string b);
string cut (string a , string b)
{
if (a[0] == '-' && b[0] == '-') return cut (- b , - a);
else if (a[0] == '-') return - gjj (- a , b);
else if (b[0] == '-') return gjj (b , - a);
else if (max (a , b) != a) return - cut (b , a);
reverse (a.begin () , a.end ());
reverse (b.begin () , b.end ());
string ans = "";
for (int i = 1;i <= a.size () + 1;i ++) ans += "0";
for (int i = 0;i < b.size ();i ++) ans[i] += a[i] - b[i];
for (int i = b.size ();i < a.size ();i ++) ans[i] = a[i];
for (int i = 0;i < ans.size ();i ++)
if (ans[i] < '0')
{
ans[i] += 10;
ans[i + 1] -= 1;
}
while (ans.size () > 1 && ans[ans.size () - 1] == '0')
ans = ans.substr (0 , ans.size () - 1);
reverse (ans.begin () , ans.end ());
return ans;
}
string gjj (string a , string b)
{
if (a[0] == '-' && b[0] == '-') return - gjc (- a , - b);
else if (a[0] == '-') return cut (b , - a);
else if (b[0] == '-') return cut (a , - b);
if (a.size () < b.size ()) swap (a , b);
reverse (a.begin () , a.end ());
reverse (b.begin () , b.end ());
string ans = "";
for (int i = 1;i <= a.size () + 1;i ++) ans += "0";
for (int i = 0;i < b.size ();i ++) ans[i] += a[i] - '0' + b[i] - '0';
for (int i = b.size ();i < a.size ();i ++) ans[i] = a[i];
for (int i = 0;i < ans.size ();i ++)
if (ans[i] > '9')
{
ans[i] -= 10;
ans[i + 1] += 1;
}
if (ans[ans.size () - 1] == '0') ans = ans.substr (0 , ans.size () - 1);
reverse (ans.begin () , ans.end ());
return ans;
}
string dev (string b , int a)
{
if (a < 0 && b[0] == '-') return dev (- b , - a);
else if (a < 0) return - dev (b , - a);
else if (b[0] == '-') return - dev (- b , a);
while (a % 10 == 0 && b[b.size () - 1] == '0')
{
a /= 10;
b = b.substr (0 , b.size () - 1);
}
string ans = "";
int yu = 0;
for (int i = 0;i < b.size ();i ++)
{
yu = yu * 10 + b[i] - '0';
ans += yu / a + '0';
yu %= a;
}
while (ans.size () > 1 && ans[0] == '0') ans = ans.substr (1);
return ans;
}
string operator / (string a , int b) {return dev (a , b);}
string operator * (string a , string b) {return gjc (a , b);}
string operator + (string a , string b) {return gjj (a , b);}
string operator - (string a , string b) {return cut (a , b);}
string sqrt (string s)
{
if (s == "0") return "0";
if (s == "1") return "1";
string l = "1" , r = "1" , ans , dan = "1";
for (int i = 1;i < s.size () / 2;i ++) l += "0";
for (int i = 1;i <= s.size () / 2 + 1;i ++) r += "0";
while (max (l , r) == r)
{
string mid = (l + r) / 2;
if (max (mid * mid , s) == s)
{
ans = mid;
l = gjj (mid , dan);
}
else r = mid - dan;
}
return ans;
}
signed main ()
{
string a , b , c = "2";
cin >> a >> b;
if (a[0] == '-' && b[0] == '-')
cout << - sqrt (a * a + b * b + c * a * b);
else if (a[0] == '-')
{
if (max (- a , b) == b) cout << sqrt (a * a + b * b + c * a * b);
else cout << - sqrt (a * a + b * b + c * a * b);
}
else if (b[0] == '-')
{
if (max (- b , a) == a) cout << sqrt (a * a + b * b + c * a * b);
else cout << - sqrt (a * a + b * b + c * a * b);
}
else cout << sqrt (a * a + b * b + c * a * b);
}二分哥写了一个二分查找。
在 [-1e9, 1e9] 里猜一个数,猜 31 次。
每一次循环都做一次比较:if(mid == a+b)。
他明明已经算了 a+b,却假装不知道。
"你考试时偷看了答案,然后假装自己算出来的。"
#include<bits/stdc++.h>
#define N 100010
using namespace std;
int a,b;
int s(int a,int b) {
int l=-1e9,r=1e9,mid;
while(l<r) {
mid=(l+r)/2;
if(mid==a+b) return mid;
else if(mid>a+b) r=mid;
else l=mid;
}
}
int main() {
cin>>a>>b;
cout<<s(a,b);
return 0;
}模拟退火哥把初始温度设为 100 万,降温系数 0.999,跑了 100 轮。
每轮 10 万次迭代,总共 1000 万次随机。
温度从 100 万降到 1e-10,终于收敛到了 3。
隔壁做物理模拟的过来围观:"兄弟你在模拟什么?"
"我在算 1+2。"
他走了。
"物理系同学:我们模拟分子运动,你们模拟什么?算加法。"
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double d=0.999;
const double lim=1e-10;
ll a,b;
ll ans;
ll num;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+(ch^48);
ch=getchar();
}
return x*f;
}
int calc(int x)
{
return abs(a+b-x)-abs(a+b-ans);
}
void ghost_fire()
{
double T=1000000;
while(T>lim)
{
int x=num+((rand()<<1)-RAND_MAX)*T;
int del=calc(x);
if(del<0)
{
ans=x;
num=x;
}
else if(exp(-del/T)>(double)rand()/RAND_MAX) num=x;
T*=d;
}
}
void work()
{
for(int i=1;i<=100;i++) ghost_fire();
}
int main()
{
a=read();
b=read();
work();
cout<<ans<<endl;
return 0;
}虚拟机哥定义了一套 5 条指令的 ISA:LOAD、ADD、STORE、PRINT、HALT。
然后写了一个 100 行的解释器。
然后写了 6 条汇编指令。
然后在虚拟机上运行。
然后得到了 3。
"为了算 1+2,我设计了一台计算机,定义了指令集,写了汇编程序,跑在虚拟机上……别人问为什么不用计算器?因为我的虚拟机没有计算器功能。"
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
enum class OpCode { LOAD, ADD, STORE, PRINT, HALT };
struct Instruction { OpCode op; int operand1; int operand2; };
class VirtualMachine {
std::vector<int> memory;
std::vector<int> registers;
size_t pc;
bool running;
public:
VirtualMachine(size_t memSize, size_t regCount)
: memory(memSize, 0), registers(regCount, 0), pc(0), running(false) {}
void execute(const std::vector<Instruction>& program) {
running = true;
while (running && pc < program.size()) {
const auto& instr = program[pc];
switch (instr.op) {
case OpCode::LOAD: registers[instr.operand1] =
instr.operand2; pc++; break;
case OpCode::ADD: registers[instr.operand1] +=
registers[instr.operand2]; pc++; break;
case OpCode::STORE: memory[instr.operand1] =
registers[instr.operand2]; pc++; break;
case OpCode::PRINT:
std::cout << memory[instr.operand1] << std::endl;
pc++; break;
case OpCode::HALT: running = false; break;
default: throw std::runtime_error("Unknown opcode");
}
}
}
};
int main() {
try {
int a, b;
std::cin >> a >> b;
VirtualMachine vm(16, 4);
std::vector<Instruction> program = {
{OpCode::LOAD, 0, a},
{OpCode::LOAD, 1, b},
{OpCode::ADD, 0, 1},
{OpCode::STORE, 0, 0},
{OpCode::PRINT, 0, 0},
{OpCode::HALT, 0, 0}
};
vm.execute(program);
return 0;
} catch (const std::exception& e) { return 1; }
}中文宏哥用 #define 把 C++ 变成了中文:
编译器把中文翻译回英文,然后悄悄编译通过。
"编译器:我翻译了一下,你就以为自己写的是中文编程了?"
#include <bits/stdc++.h>
using namespace std;
#define 开始啦 main()
#define 开头 {
#define 结尾 }
#define 和 ,
#define 没了 return 0
#define 整数32位 int
#define 输入 cin
#define 输出 cout
#define 一个 >>
#define 这个 <<
#define 加上 +
#define 换行 '\n'
整数32位 开始啦
开头
整数32位 a 和 b;
输入 一个 a 一个 b;
输出 这个 a 加上 b 这个 换行;
没了;
结尾文言文哥用 wenyan-lang 写了 A+B:
历史老师以为他在写《史记》,语文老师以为他在写古诗,编译器说:
"我只能翻译成 JavaScript。"
"三脸懵逼:历史、语文、编译器。"
施「require('fs').readFileSync」於「「/dev/stdin」」。名之曰「數據」。
施「(buf => buf.toString().trim())」於「數據」。昔之「數據」者。今其是矣。
施「(s => s.split(' '))」於「數據」。昔之「數據」者。今其是矣。
注曰。「「文言尚菜,無對象之操作,故需 JavaScript 之语法」」。
夫「數據」之一。取一以施「parseInt」。名之曰「甲」。
夫「數據」之二。取一以施「parseInt」。名之曰「乙」。
加「甲」以「乙」。書之。这些人,有的写了 200 行,有的写了 1000 万次迭代,有的定义了一台虚拟机。
他们本可以写一行简单的加法,但他们没有。
他们把简单的问题复杂化,把复杂的代码优雅化,把优雅的算法浪漫化。
他们不是在算 A+B。
他们是在向世界证明:
程序员的浪漫,就是把一道入门题,写成一部史诗。
| 流派 | 代表作 | 全体致敬 |
|---|---|---|
| LCT | 动态树算加法 | 相识->分手->复合,比偶像剧还曲折 |
| 线段树 | 标记永久化 | 用集装箱存一颗芝麻 |
| 树状数组 | 50万空间存1个数 | 储物柜空着499999格 |
| Splay | 序列翻转 | 翻转证明交换律,数学老师哭了 |
| Treap | 插200万个1 | CPU风扇转得比你脑子快 |
| Dijkstra | 三条边跑最短路 | 导航软件:我这就下岗? |
| Kruskal | INF边强行加戏 | 边:我不要面子的吗? |
| 完全平方 | 高精度开根 | 数学老师:公式不是这么用的 |
| 二分查找 | 猜答案 | 偷看答案假装自己算的 |
| 模拟退火 | 1000万次随机 | 物理系同学:你在模拟啥?算1+2 |
| 虚拟机 | 手搓ISA | 为了喝牛奶养了一头牛 |
| 中文宏 | #define 中文 | 编译器:我翻译了一下 |
| 文言文 | 史记风代码 | 历史/语文/编译器三脸懵逼 |
某日,一位萌新点开 A+B 的题解区。
他看到了 LCT、Splay、模拟退火、虚拟机、文言文……
他沉默了。
然后他写下了人生中第一行代码:
cout << a + b;他 AC 了。
他在那一刻突然明白:
所有的群星,都是在为最简单的真理闪烁。
而最简单的真理,往往只需要一行代码。
THE END