https://www.luogu.com.cn/problem/P3381
鉴于spfa很容易被卡,所以学了一手dij跑费用流
具体参考:https://www.luogu.com.cn/blog/Mogician/Network-Flow-Guide,虽然有个小地方少了个负号,不过还好
区别就是把(u->v)边权变成cost+h[u]-h[v],先用spfa算出dis当h[i],然后每次跑完mcf,由于负权边的容量可能增加能选了,就要h[i]+=dis[i]这样动态维护,就能保证每条边都是正的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> p;
const int maxl=5010;
const int maxm=1e5+10;
const ll inf=1ll<<60;
int n,m,S,T,cnt;
ll ans,flow;
int ehead[maxl],frm[maxl];
ll dis[maxl],h[maxl];
struct ed
{
int frm,to,nxt;ll w,c;
}e[maxm<<1];
bool in[maxl];
inline void add(int u,int v,ll w,ll c)
{
e[++cnt].to=v;e[cnt].frm=u;
e[cnt].w=w;e[cnt].c=c;
e[cnt].nxt=ehead[u];ehead[u]=cnt;
}
inline void prework()
{
scanf("%d%d%d%d",&n,&m,&S,&T);
int u,v;ll w,c;cnt=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld%lld",&u,&v,&w,&c);
add(u,v,w,c);add(v,u,0,-c);
}
}
inline void spfa()
{
queue<int> q;
for(int i=1;i<=n;i++)
h[i]=inf,in[i]=false;
q.push(S);h[S]=0;in[S]=true;
int u,v;
while(!q.empty())
{
u=q.front();q.pop();in[u]=false;
for(int i=ehead[u];i;i=e[i].nxt)
if(e[i].w)
{
v=e[i].to;
if(h[v]>h[u]+e[i].c)
{
h[v]=h[u]+e[i].c;
if(!in[v])
{
q.push(v);
in[v]=true;
}
}
}
}
}
inline bool dij()
{
priority_queue<p,vector<p>,greater<p> >q;
for(int i=1;i<=n;i++)
dis[i]=inf,in[i]=false,frm[i]=0;
dis[S]=0;q.push({dis[S],S});
int u,v;p d;
while(!q.empty())
{
d=q.top();q.pop();
u=d.second;
if(in[u] || dis[u]!=d.first)
continue;
in[u]=true;
for(int i=ehead[u];i;i=e[i].nxt)
if(e[i].w)
{
v=e[i].to;
if(in[v] || dis[v]<=dis[u]+e[i].c+h[u]-h[v])
continue;
frm[v]=i;
dis[v]=dis[u]+e[i].c+h[u]-h[v];
q.push({dis[v],v});
}
}
return dis[T]<inf;
}
inline void mcf()
{
int i=T;ll x=inf;
i=frm[T];
while(i>0)
{
x=min(x,e[i].w);
i=frm[e[i].frm];
}
i=frm[T];
while(i>0)
{
e[i].w-=x;
e[i^1].w+=x;
i=frm[e[i].frm];
}
flow+=x;
ans+=x*(dis[T]+h[T]);
}
inline void mainwork()
{
ans=0;
spfa();
while(dij())
{
mcf();
for(int i=1;i<=n;i++)
h[i]=min(inf,h[i]+dis[i]);
}
}
inline void print()
{
printf("%lld %lld",flow,ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}