P2469 [SDOI2010]星际竞速
pro:
https://www.luogu.com.cn/problem/P2469
sol:
发现这个模型和最小路径覆盖有着密切的联系
考虑最小路径覆盖是怎么实现的
一开始用n条长度为1的路径去覆盖n个点
使初始答案为n
每个点拆成入点和出点
对于原图每条边x--->y
然后x的出点和y的入点连边
跑二分图匹配
求最大流flow
则ans=n-flow
因为每匹配两条边就等价于把x所在的路径和y所在路径连接在一起
答案应该-1
再考虑本题
还是先考虑n条路径的情况
答案显然为\(\sum_{i=1}^n a_i\)
然后考虑连接两条路径时能省掉多少代价
发现新增了边权\(w\),减少了新开一条路径的费用\(a_y\)
因此这个问题和最小路径类似
只不过不在是简单的二分图最大匹配
而是变成了一个带权二分图最大匹配
这个玩意显然就是个最大费用流
冲就完事了
#include<bits/stdc++.h>
#define N 11000
#define M 110000
#define eps 1e-7
#define inf 1e12
#define db double
#define ll long long
#define ldb long double
#define ull unsigned long long
using namespace std;
inline ll read()
{
char ch=0;
ll x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
return x*flag;
}
struct edge
{
ll to,nxt,flow,w;
}e[M];
ll num,head[N];
inline void add(ll x,ll y,ll w,ll z)
{
e[++num]=(edge){y,head[x],w,+z};head[x]=num;
e[++num]=(edge){x,head[y],0,-z};head[y]=num;
}
bool in_queue[N];
ll n,m,s,t,dis[N],pre[N],last[N],flow[N];
queue<ll>q;
bool spfa()
{
for(ll i=0;i<=t;i++)dis[i]=-inf,flow[i]=+inf;
dis[s]=0;q.push(s);in_queue[s]=true;
while(!q.empty())
{
ll x=q.front();q.pop();in_queue[x]=false;
for(ll i=head[x];i!=-1;i=e[i].nxt)
{
ll to=e[i].to;
if(e[i].flow&&dis[to]<dis[x]+e[i].w)
{
pre[to]=x;last[to]=i;
dis[to]=dis[x]+e[i].w;
flow[to]=min(flow[x],e[i].flow);
if(!in_queue[to])q.push(to),in_queue[to]=true;
}
}
}
return dis[t]>0;
}
void dfs(ll x)
{
if(x==s)return;
ll i=last[x];
e[i].flow-=flow[t];
e[i^1].flow+=flow[t];
dfs(pre[x]);
}
ll a[N];
int main()
{
n=read();m=read();s=2*n+1;t=2*n+2;
num=-1;memset(head,-1,sizeof(head));
ll ans=0;
add(s,t,+inf,0);
for(ll i=1;i<=n;i++)
{
a[i]=read();
add(s,i,1,0);
add(i+n,t,1,0);
ans+=a[i];
}
for(ll i=1;i<=m;i++)
{
ll x=read(),y=read(),z=read();
if(x>y)swap(x,y);
if(a[y]-z>0)add(x,y+n,1,a[y]-z);
}
while(spfa())ans-=dis[t]*flow[t],dfs(t);
printf("%lld",ans);
return 0;
}