LINK

给定 m m m条边,每个点有点权为 a i a_i ai

要求你为每条边定向,最小化

D = ∑ i = 1 n m a x ( 0 , d i − a i ) D=\sum\limits_{i=1}^nmax(0,d_i-a_i) D=i=1nmax(0,diai)


对于一条边 ( l , r ) (l,r) (l,r)要么指向 l l l要么指向 r r r

对于点 l l l来说,前 a i a_i ai条指向自己的边对 D D D没有任何贡献

从第 a i + 1 a_i+1 ai+1条边开始,每多一条边,权值增加 1 1 1

那么其实跑一个最小费用流就好了

因为对于每个点肯定是先走免费的 a i a_i ai条边再走收费边,符合权值的计算方式

具体连边

把第 i i i条边抽象为一个点 m i m_i mi,这条边连接着点 l i , r i l_i,r_i li,ri

源点 s s s连向每条边 m i m_i mi,流量为 1 1 1费用 0 0 0

m i m_i mi分别连向 l i , r i l_i,r_i li,ri,流量都是 1 1 1,费用都是 0 0 0

对于每个点 u u u, u u u连一条流量 a u a_u au费用 0 0 0的边到汇点,再连一条流量 i n f inf inf费用 1 1 1的边到汇点

跑一个最小费用最大流就是答案

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+10;
const int inf = 1e9;
int dis[maxn],vis[maxn],flow[maxn],pre[maxn],id[maxn];
int a[maxn],l[maxn],r[maxn];
int n,m,s,t;
struct edge
{
	int to,nxt,flow,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow,int w)
{
	d[++cnt] = ( edge ){v,head[u],flow,w},head[u] = cnt;
	d[++cnt] = ( edge ){u,head[v],0,-w},head[v] = cnt;
}
bool spfa(int s,int t,int mx)
{
	for(int i=0;i<=mx;i++)	dis[i] = inf, vis[i] = 0;
	queue<int>q; q.push( s );
	dis[s] = 0, flow[s] = inf;
	while( !q.empty() )
	{
		int u = q.front(); q.pop();
		vis[u] = 0;
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v = d[i].to;
			if( d[i].flow && dis[u]+d[i].w<dis[v] )
			{
				dis[v] = dis[u]+d[i].w, pre[v] = i;
				flow[v] = min( d[i].flow,flow[u] );
				if( !vis[v] )	q.push( v ), vis[v] = 1;
			}
		}
	}
	return dis[t]!=inf;
}
int MCMF(int s,int t,int mx)
{
	int mincost = 0;
	while( spfa(s,t,mx) )
	{
		mincost += dis[t]*flow[t];
		int x = t, i;
		while( x!=s )
		{
			i = pre[x];
			d[i].flow -= flow[t], d[i^1].flow += flow[t];
			x = d[i^1].to;
		}
	}
	return mincost;
}
void build()
{
	for(int i=1;i<=m;i++)
	{	
		add(s,i,1,0);
		id[i] = cnt+1; add(i,m+l[i],1,0); add(i,m+r[i],1,0);
	}
	for(int i=1;i<=n;i++)
		add(m+i,t,a[i],0),add(m+i,t,inf,1);
}
int main()
{
	int T; cin >> T;
	while( T-- )
	{
		cin >> n >> m;
		s = 0, t = n+m+1;
		for(int i=1;i<=n;i++)	scanf("%d",&a[i] );
		for(int i=1;i<=m;i++)	scanf("%d%d",&l[i],&r[i] );
		build();
		cout << MCMF(s,t,n+m+1) << endl;
		for(int i=1;i<=m;i++)
			printf("%d",d[id[i]].flow==0?1:0);
		puts("");
		cnt =  1;
		for(int i=0;i<=t;i++)	head[i] = 0;
	}
}