题目大意
给出 (n) 个用户,(m) 个基站,每个用户的需求为用 (a,b) 两个基站,会产生 (c) 的收益。修基站也需要花费。问最大收益。
(nle 5000,mle 50000)
思路
这个东西要用一个叫做最大权闭合子图的东西。大概意思就是求解,对于一个图,每个点有点权,你选了一个点则它的后继都必须选,问选出来的点权值之和最大是多少。对于这个问题我们可以最小割来做,(s) 往所有点权为正的点连,流量为点权,所有点权为负的点往 (t) 连,流量为点权绝对值。原图上的边流量改为 (infty)。然后答案就是所有边权为正的权值之和-最小割。
这个模型的含义就是:
对于 (s) 连向的点,如果割了这个边,相同于不选。
对于连向 (t) 的点,如果割了这个边,相当于选。
正确性和最优性应该显然,应该每个方案都与原图上的方案一一对应。
对于这个点,我们可以直接用户连向基站就好了,用户点权为收益,基站点权为-花费。然后直接套用上面的最大权闭合子图即可。
( exttt{Code})
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 500005
#define MAXN 60005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int n,m,P[MAXN];
struct edge{
int v,flw,nxt;
}e[MAXM];
int toop = 1,S,T,dep[MAXN],cur[MAXN],head[MAXN];
void Add_Edge (int u,int v,int w){
e[++ toop] = edge {v,w,head[u]},head[u] = toop;
e[++ toop] = edge {u,0,head[v]},head[v] = toop;
}
bool BFS (){
memset (dep,0,sizeof (dep));
queue <int> q;q.push (S),dep[S] = 1;
while (!q.empty()){
int u = q.front();q.pop ();
for (Int i = head[u];i;i = e[i].nxt){
int v = e[i].v,w = e[i].flw;
if (!dep[v] && w) dep[v] = dep[u] + 1,q.push (v);
}
}
return dep[T];
}
int dfs (int u,int flow){
if (u == T) return flow;
int ans = 0;
for (Int &i = cur[u];i && flow;i = e[i].nxt){
int v = e[i].v,f = e[i].flw;
if (dep[v] != dep[u] + 1) continue;
int res = dfs (v,min (flow,f));e[i].flw -= res,e[i ^ 1].flw += res,ans += res,flow -= res;
}
if (!flow) dep[u] = 0;
return ans;
}
int MinCut (){
int res = 0;
while (BFS ()){
memcpy (cur,head,sizeof (head));
res += dfs (S,INF);
}
return res;
}
signed main(){
read (n,m),S = n + m + 1,T = n + m + 2;
for (Int i = 1;i <= n;++ i) read (P[i]),Add_Edge (m + i,T,P[i]);int ans = 0;
for (Int i = 1,a,b,c;i <= m;++ i) read (a,b,c),Add_Edge (i,m + a,INF),Add_Edge (i,m + b,INF),Add_Edge (S,i,c),ans += c;
write (ans - MinCut()),putchar ('
');
return 0;
}