- A+B问题
恶搞题解
- @ 2025-10-19 10:42:04
(来源于洛谷) 神题,做法多种多样。
首先,我们可以发现这题有个最简单的做法,就是读入 a,b,输出 a+b 即可。代码如下:
namespace solver1
{
int a,b;
inline void solve()
{
a=read(),b=read();
cout << a+b << endl;
}
}
虽然能 AC,但是洛谷上很多神题,因此我觉得这题不应该这么简单。
首先,这个题很明显可以不用加号,因此有如下做法(自欺欺人):
namespace solver2
{
int a,b;
inline void solve()
{
a=read(),b=read();
cout << a-(-b) << endl;
}
}
我们还可以考虑如何不用 +,- 两个符号完成这个题。考虑一下进行加法的本质是每一个位上的进位,而要产生进位,也就是两个对应位上都是 1 才会产生进位。这个是可以通过 a&b 得到的。而如果我们进行 a^b 则可以得到两个位在不考虑其他位上的进位的时候的加法计算结果。因为 0^1=1^0=1,相当于没有进位的加 1,而 1^1=0,相当于 1+1 产生进位,而 0^0=0 相当于两位都是 0,相加也是 0。因此我们可以使用异或以及与运算完成加法。具体请看代码。
namespace solver3
{
int a,b;
inline void solve()
{
a=read(),b=read();
while (b!=0)
{
int _a=a^b;
int _b=(a&b)<<1;
a=_a;
b=_b;
}
cout << a << endl;
}
}
接下来有一些别的方法。
首先,如果我们有两个集合 {A},{B},其中 ∣A∣=a,∣B∣=b,我们把两个集合合并,得到集合的秩的大小,那么就可以很好地完成了。
反正我们也不知道我们集合里面是什么东西就干脆用并查集吧,不用开个 set。
namespace solver4
{
int a[5],fa[5],size[5];
inline void Init()
{
for (int i=1;i<=2;i++)
{
fa[i]=i;
size[i]=a[i];
}
}
inline int Find(int x)
{
return fa[x]==x?x:fa[x]=Find(fa[x]);
}
inline void Merge(int x,int y)
{
int fx=Find(x),fy=Find(y);
if (fx!=fy)
{
fa[fy]=fx;
size[fx]+=size[fy];
}
}
inline void solve()
{
a[1]=read(),a[2]=read();
Init();
Merge(1,2);
cout << size[1] << endl;
}
}
我们还可以考虑,如果目前有一张图,有 3 个节点 1→2,2→3,那么令每条边的边权分别是 a,b,那么最后的答案就是这个图的最小生成树,当然最大生成树也是可以的。因为我只会 kruskal,所以代码如下:
namespace solver5
{
int a[5],fa[5];
struct node
{
int u,v,w;
bool operator < (const node &rhs) const
{
return w<rhs.w;
}
}edge[5];
inline void Init()
{
for (int i=1;i<=2;i++)
fa[i]=i;
}
inline int Find(int x)
{
return fa[x]==x?x:fa[x]=Find(fa[x]);
}
inline void Merge(int x,int y)
{
int fx=Find(x),fy=Find(y);
if (fx!=fy)
fa[fy]=fx;
}
inline void solve()
{
a[1]=read(),a[2]=read();
Init();
sort(a+1,a+3);
edge[1]={1,2,a[1]};
edge[2]={2,3,a[2]};
int ans=0;
for (int i=1;i<=2;i++)
{
auto e=edge[i];
int u=edge[i].u,v=edge[i].v;
int fu=Find(u),fv=Find(v);
if (fu!=fv)
{
ans+=e.w;
Merge(fu,fv);
}
}
cout << ans << endl;
}
}
当然,如果你真建立出这张图,那么我们甚至可以使用 dijkstra 跑从 1→3 的最短路径,也可以算出我们要求的答案。
namespace solver6
{
struct node
{
int to,dis;
bool operator < (const node &rhs) const
{
return dis<rhs.dis;
}
};
priority_queue <node> q;
vector <node> G[5];
int dis[5],vis[5];
inline void Dijkstra()
{
memset(dis,0x7f,sizeof(dis));
q.push({1,0});
dis[1]=0;
while (!q.empty())
{
node rhs=q.top();
q.pop();
int u=rhs.to;
if (vis[u])
continue;
for (int i=0;i<G[u].size();i++)
{
rhs=G[u][i];
int v=rhs.to,d=rhs.dis;
if (dis[v]>dis[u]+d)
{
dis[v]=dis[u]+d;
q.push({v,dis[v]});
}
}
}
}
inline void solve()
{
int a=read(),b=read();
G[1].push_back({2,a});
G[2].push_back({3,b});
Dijkstra();
cout << dis[3] << endl;
}
}
当然,有一些很奇妙的方法。我们考虑一下网络流解法。当然因为里面有负权,所以应该取绝对值然后判断要不要减回去。
第一种解法是最大流。两条边流量分别是 a,b,这样的确就可以得到 a+b。
namespace solver7
{
int head[50],tail[50],next[50],r[50],ecnt=1,s=1,t=4,d[50],cur[50],flow;
inline void link(int u,int v,int w)
{
next[++ecnt]=head[u];
head[u]=ecnt;
tail[ecnt]=v;
r[ecnt]=w;
next[++ecnt]=head[v];
head[v]=ecnt;
tail[ecnt]=u;
r[ecnt]=0;
}
inline bool BFS()
{
queue <int> q;
q.push(t);
memset(d,0,sizeof(d));
d[t]=1;
while (!q.empty())
{
int u=q.front();
q.pop();
for (int i=head[u];i;i=next[i])
{
int v=tail[i];
if (d[v]==0 && r[i^1]>0)
{
d[v]=d[u]+1;
q.push(v);
}
}
}
return d[s]>0;
}
inline int DFS(int u,int budget)
{
if (u==t)
return budget;
int res=0;
for (int &e=cur[u];e;e=next[e])
{
int v=tail[e];
if (d[v]+1==d[u] && r[e]>0)
{
int delta=DFS(v,min(r[e],budget));
if (delta)
{
r[e]-=delta;
r[e^1]+=delta;
flow+=delta;
budget-=delta;
res+=delta;
}
}
}
return res;
}
inline int Dinic()
{
int ans=0;
while (BFS())
{
for (int i=s;i<=t;i++)
cur[i]=head[i];
ans+=DFS(s,1145141919);
}
return ans;
}
inline void solve()
{
int f=0;
int a=read(),b=read();
if (a<0)
{
a=abs(a);
f|=1;
}
if (b<0)
{
b=abs(b);
f|=2;
}
link(1,2,a);
link(1,3,b);
link(2,4,1145141919);
link(3,4,1145141919);
int ans=Dinic();
if (f&1)
ans-=2*a;
if (f&2)
ans-=2*b;
cout << ans << endl;
}
}
第二种解法是费用流。我们考虑一条边的费用是 a,一条边的费用是 b,单位流量的费用为 1,则可以使用最小费用最大流解决,在这里我采用的是原始对偶 (primal-dual) 算法求费用流。好像写原始对偶是不用判负了(
namespace solver8
{
struct node
{
int to,dis;
bool operator < (const node &rhs) const
{
return dis>rhs.dis;
}
};
int d[10],preu[10],pree[10];
bool inq[10];
int e=1,w[10],r[10],first[10],next[10],cost=0,flow=0,tail[10],h[10];
void link(int u,int v,int re,int weight)
{
next[++e]=first[u];
first[u]=e;
tail[e]=v;
r[e]=re;
w[e]=weight;
next[++e]=first[v];
first[v]=e;
tail[e]=u;
r[e]=0;
w[e]=-weight;
}
int s=1,t=3;
bool vis[100050];
inline void dijkstra()
{
for (int i=1;i<=5;i++)
{
vis[i]=0;
d[i]=INT_MAX;
}
d[s]=0;
priority_queue <pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;
q.empty();
q.push(pair<int,int>(0,s));
while (!q.empty())
{
pair <int,int> rhs=q.top();
q.pop();
int u=rhs.second;
if (vis[u])
continue;
for (int i=first[u];i;i=next[i])
{
int v=tail[i];
if (r[i]>0 && d[v]>d[u]+w[i]+h[u]-h[v])
{
d[v]=d[u]+w[i]+h[u]-h[v];
preu[v]=u;
pree[v]=i;
q.push(pair<int,int>(d[v],v));
}
}
}
}
inline void MinCostMaxFlow()
{
memset(h,0,sizeof(h));
while (1)
{
dijkstra();
if (d[t]==INT_MAX)
break;
else
{
for (int u=1;u<=3;u++)
h[u]+=d[u];
int delta=INT_MAX;
for (int u=t;u!=s;u=preu[u])
{
int e=pree[u];
delta=min(delta,r[e]);
}
flow+=delta;
cost+=delta*h[t];
for (int u=t;u!=s;u=preu[u])
{
int e=pree[u];
r[e]-=delta;
r[e^1]+=delta;
}
}
}
}
inline void solve()
{
int a=read(),b=read();
link(1,2,1,a);
link(2,3,1,b);
MinCostMaxFlow();
cout << cost << endl;
}
}

namespace solver9
{
int a[5],S[5];
inline void solve()
{
for (int i=1;i<=2;i++)
a[i]=read();
for (int i=1;i<=2;i++)
S[i]=S[i-1]+a[i];
cout << S[2]-S[0] << endl;
}
}
当然如果只是读入 a[i] 的话显得不高级。我们考虑几个可以维护区间的数据结构。
首先是树状数组,单点修改,区间查询和:
namespace solver10
{
int f[10];
inline int Lowbit(int x)
{
return x&-x;
}
inline void Change(int k,int x)
{
while (k<=5)
{
f[k]+=x;
k+=Lowbit(k);
}
}
inline int Query(int k)
{
int ans=0;
while (k)
{
ans+=f[k];
k-=Lowbit(k);
}
return ans;
}
inline void solve()
{
Change(1,read());
Change(2,read());
cout << Query(2) << endl;
}
}
然后是线段树区间修改区间求和。
namespace solver11
{
struct SegTree
{
int l,r;
long long val,tag;
}t[50];
inline void Push_Up(int id)
{
t[id].val=t[id<<1].val+t[id<<1|1].val;
}
inline void Push_Down(int id)
{
if (t[id].tag)
{
int mid=(t[id].l+t[id].r)>>1;
t[id<<1].tag+=t[id].tag;
t[id<<1|1].tag+=t[id].tag;
t[id<<1].val+=(mid-t[id].l+1)*t[id].tag;
t[id<<1|1].val+=(t[id].r-mid)*t[id].tag;
t[id].tag=0;
}
}
inline void Build(int id,int l,int r)
{
t[id].l=l;
t[id].r=r;
if (l==r)
return;
int mid=(l+r)>>1;
Build(id<<1,l,mid);
Build(id<<1|1,mid+1,r);
Push_Up(id);
}
inline void Change(int id,int l,int r,int val)
{
if (l<=t[id].l && t[id].r<=r)
{
t[id].tag+=val;
t[id].val+=val*(t[id].r-t[id].l+1);
return;
}
Push_Down(id);
int mid=(t[id].l+t[id].r)>>1;
if (r<=mid)
Change(id<<1,l,r,val);
else if (l>mid)
Change(id<<1|1,l,r,val);
else
{
Change(id<<1,l,mid,val);
Change(id<<1|1,mid+1,r,val);
}
Push_Up(id);
}
inline long long Query(int id,int l,int r)
{
if (l<=t[id].l && t[id].r<=r)
return t[id].val;
Push_Down(id);
int mid=(t[id].l+t[id].r)>>1;
if (r<=mid)
return Query(id<<1,l,r);
else if (l>mid)
return Query(id<<1|1,l,r);
else
return Query(id<<1,l,mid)+Query(id<<1|1,mid+1,r);
}
inline void solve()
{
Build(1,1,2);
Change(1,1,1,read());
Change(1,2,2,read());
cout << Query(1,1,2) << endl;
}
}
那么平衡树也可以做。
0 条评论
信息
- ID
- 1
- 时间
- ms
- 内存
- MiB
- 难度
- 1
- 标签
- 递交数
- 252
- 已通过
- 71
- 上传者