解析
熟练和固化在有些时候是等价的。
一个看起来喜闻乐见的模型。
n2n^2n2 信息量你在逗我…
结果是:点数 n2n^2n2 TLE,边数 n2n^2n2 AC。
一种之前所没有见过的打开方式。
还是考虑最小割模型,点 iii 向原点连一条 AiA_iAi 的边,断则表示雇佣。
不同的是,不再对每一对 (i,j)(i,j)(i,j) 开虚点,而是直接从 iii 向汇点连一条 ∑Ei,j\sum E_{i,j}∑Ei,j 的边,表示 iii 获得了所有的加成收益。
然后我们需要补偿,如果 jjj 没有雇佣,那么 iii 不仅无法获得收益,还会付出代价,两相做差,应该连一条 (j,i,2Ei,j)(j,i,2E_{i,j})(j,i,2Ei,j) 的边。
即可。
思维打开!
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;const int N=5e6+100;
const ll inf=2e12;
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n,m,id;int s,t,tot;
struct node{int to,nxt;ll cap;
}p[N<<1];
int fi[N],cur[N],cnt;
inline void Addline(int x,int y,ll cap){p[++cnt]=(node){y,fi[x],cap};fi[x]=cnt;
}
inline void add(int x,int y,ll c){Addline(x,y,c);Addline(y,x,0);
}
int bel[N];
int q[N],st,ed;
int bfs(){memset(bel,0,sizeof(int)*(tot+1));bel[s]=1;q[st=ed=1]=s;while(st<=ed){int now=q[st++];for(int i=cur[now]=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(!p[i].cap||bel[to]) continue;bel[to]=bel[now]+1;q[++ed]=to;}}return bel[t];
}
ll dfs(int x,ll lim){if(x==t||!lim) return lim;ll res(0);for(int &i=cur[x];~i;i=p[i].nxt){int to=p[i].to;if(bel[to]!=bel[x]+1) continue;ll add=dfs(to,min(lim,p[i].cap));res+=add;lim-=add;p[i].cap-=add;p[i^1].cap+=add;if(!lim) break;}if(!res) bel[x]=-1;return res;
}
ll dinic(){ll flow(0),tmp(0);while(bfs()){while((tmp=dfs(s,inf))) flow+=tmp;}return flow;
}ll w[N];
signed main(){#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifmemset(fi,-1,sizeof(fi));cnt=-1;tot=n=read();s=++tot;t=++tot;ll ans(0);for(int i=1;i<=n;i++){int x=read();add(s,i,x);}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){ll x=read();ans+=x;add(i,j,x*2);w[i]+=x;}}for(int i=1;i<=n;i++) add(i,t,w[i]);printf("%lld\n",ans-dinic());return 0;
}