P3973 [TJOI2015]线性代数 最小割
题意:
分析:
\[D=\sum_{j=1}^nA_{1,j}\times (\sum_{i=1}^nA_{1,i}B_{i,j}-C_{1,j})
\]
我们观察式子可以发现 \(B_{i,j}\) 会被选当且仅当 \(A_{1,i},A_{1,j}\) 都为 1,\(-C_{1,j}\) 会被选当且仅当 \(A_{1,j}\) 为 1
也就是说 \(B_{i,j}\) 和 \(-C_{1,i},-C_{1,j}\) 必须同时存在,也就是说选了 \(B_{i,j}\) 就得减少 \(C_{1,i}+C_{1,j}\) 这个东西好像很最小割,我们只要把 \(B_{i,j}\) 到汇点路径上的流量表示为 \(C_{1,i}+C_{1,j}\) 那么要么不选 \(B_{i,j}\) 要么必须减少 \(C_{1,i}+C_{1,j}\)
具体做法就是:
- 对于每一个 \(B_{i,j},C_{1,i}\) 建立一个点
- 源点向所有的 \(B_{i,j}\) 连一条流量为 \(B_{i,j}\) 的边,\(C_{1,i}\) 向汇点连一条流量为 \(C_{1,i}\) 的边
- 每一个 \(B_{i,j}\) 向 \(C_{1,i},C_{1,j}\) 连一条流量为 \(\inf\) 的边
- 求最小割,答案等于 \(\sum B_{i,j}-mincut\)
代码:
#include<bits/stdc++.h>
#define inl inline
#define reg register
#define id(i,j) (i-1)*n+j
using namespace std;
namespace zzc
{
typedef long long ll;
inl ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const ll maxn = 3e5+5;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll n,cnt=1,st,ed,ans;
ll head[maxn],dep[maxn],cur[maxn];
queue<ll> q;
struct edge
{
ll to,nxt,w;
}e[maxn<<3];
inl void add(ll u,ll v,ll w)
{
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
inl void add_edge(ll u,ll v,ll w)
{
add(u,v,w);add(v,u,0);
}
inl bool bfs()
{
for(reg ll i=st;i<=ed;i++) dep[i]=-1;
dep[st]=0;q.push(st);
while(!q.empty())
{
ll u=q.front();q.pop();
for(reg ll i=head[u];i;i=e[i].nxt)
{
ll v=e[i].to;
if(e[i].w&&dep[v]==-1)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[ed]!=-1;
}
ll dfs(ll u,ll flow)
{
if(u==ed) return flow;
ll w,used=0;
for(reg ll &i=cur[u];i;i=e[i].nxt)
{
ll v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1)
{
w=dfs(v,min(flow-used,e[i].w));
e[i].w-=w;
e[i^1].w+=w;
used+=w;
if(used==flow) return used;
}
}
if(!used) dep[u]=-1;
return used;
}
inl void dinic()
{
while(bfs())
{
memcpy(cur,head,sizeof(head));
ans-=dfs(st,inf);
}
}
void work()
{
ll a;
n=read();st=0;ed=n*n+n+1;
for(reg ll i=1;i<=n;i++) for(reg ll j=1;j<=n;j++) a=read(),ans+=a,add_edge(st,id(i,j),a),add_edge(id(i,j),id(n+1,i),inf),add_edge(id(i,j),id(n+1,j),inf);
for(reg ll i=1;i<=n;i++) a=read(),add_edge(id(n+1,i),ed,a);
dinic();
printf("%lld\n",ans);
}
}
int main()
{
zzc::work();
return 0;
}