CF1082G Petya and Graph
考虑如果我们选了一条边,那么边连接的两个端点也一定要选
我们选边得到正的价值,选点得到负的价值
发现就是求一个最大权闭合子图
把边也看成点,S向它连边,边权为它的价值
然后向 原边连接的两点 连权值 INF 的边
最后把原图的点连向 T ,边权为点值(正数)
然后跑最小割
解释:
因为中间的边为 INF ,所以不考虑割它们
如果割 S 出发的边的边相当于我们放弃了此原边而可以不选原边连接的两点,要扣掉原边的价值
如果割到 T 的边则相当于我们为了某些原边而选择这个原点,当然要扣掉点的价值
这样最后答案就是总边权减最小割
注意 long long
#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=1e5+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; ll 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]; } ll dfs(int x,ll mif) { if(x==T||!mif) return mif; ll 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,1ll*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(i,T,read()); for(int i=1;i<=m;i++) { a=read(); b=read(); c=read(); add(n+i,a,INF); add(n+i,b,INF); add(S,n+i,c); ans+=c; } while(BFS()) ans-=dfs(S,1e18); printf("%lld",ans); return 0; }