P4174 [NOI2006]最大获利
把用户群和中转站都看成点
用户群权值为正,中转站权值为负
为了获得用户群的权值,我们不得不一起获得中转站负的权值
发现就是裸的最大权闭合子图
那么从用户群连边向中转站,边值INF
从 S 连向用户群,边权为用户群权值
最后从中转站连向 T 边权为中转站权值的绝对值
然后直接最小割
不懂原因的去学一下最大权闭合子图
不解释
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> using namespace std; typedef long long ll; inline 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<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=4e5+7,INF=1e9+7; int fir[N],from[N<<1],to[N<<1],val[N<<1],cntt=1,Fir[N]; inline void add(int a,int b,int c) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; val[cntt]=c; from[++cntt]=fir[b]; fir[b]=cntt; to[cntt]=a; val[cntt]=0; } int n,m,S,T; int ans; int dep[N]; queue <int> q; int BFS() { for(int i=1;i<=T;i++) dep[i]=0; q.push(S); dep[S]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(dep[v]||!val[i]) continue; q.push(v); dep[v]=dep[x]+1; } } for(int i=1;i<=T;i++) Fir[i]=fir[i]; return dep[T]; } int dfs(int x,int mif) { if(x==T||!mif) return mif; int fl=0,res=0; for(int i=Fir[x];i;i=from[i]) { Fir[x]=i; int &v=to[i]; if(dep[v]!=dep[x]+1) continue; if( res=dfs(v,min(mif,val[i])) ) { fl+=res; mif-=res; val[i]-=res; val[i^1]+=res; if(!mif) break; } } return fl; } int main() { int a,b,c; n=read(),m=read(); S=n+m+1; T=n+m+2; for(int i=1;i<=n;i++) add(S,i,read()); for(int i=1;i<=m;i++) { a=read(); b=read(); c=read(); add(a,n+i,INF); add(b,n+i,INF); add(n+i,T,c); ans+=c; } while(BFS()) ans-=dfs(S,INF); printf("%d",ans); return 0; }