题目

题目链接:https://www.ybtoj.com.cn/contest/117/problem/2
【YbtOJ#582】大收藏家_c++
\(T\leq 10,n,m,a_i\leq 3000\)

思路

我们只需要最大化 \(1\) 最终藏品数量,那么显然所有人的藏品可以看作只有一个黑色,剩余 \(a_i-1\) 个全部都是没有用的白色。我们需要最大化最后 \(1\) 号黑色的藏品数量。
考虑如果两个人交换两个黑色的,那么其实等价于没有交换。如果交换两个白色的,那么毫无意义。所以我们肯定只让一个黑色和一个白色进行交换。
所以我们可以把白色看作是格子,也就是任何人任何时刻的物品不能超过 \(a_i\) 个,每一次的“交换”其实可以看做一个人把自己的物品给另一个人。
这很符合一个网络流模型。把所有人拆成 \(m+1\) 个点,从源点向所有人第一个点连一条流量为一的边。对于第 \(i\) 个时刻,每一个人的第 \(i\) 个点向第 \(i+1\) 个点连一条流量为 \(a_i\) 的边,限制最多只能有 \(a_i\) 个物品。如果 \(x\)\(y\) 在第 \(i\) 时刻交换物品,那么就在 \(x,y\) 的第 \(i\) 号点之间连一条流量为 \(1\) 的双向边。最后 \(1\) 的第 \(m+1\) 个点向汇点连边。
但是这样点数是 \(O(nm)\) 的,不可接受。我们发现每一个时刻只需要连这个时刻有关的人的点就可以了。这样点数就降到了 \(O(n+m)\)
时间复杂度 \(O(Tnm)\)

代码
#include <bits/stdc++.h>
using namespace std;

const int N=10010,Inf=1e9;
int Q,S,T,n,m,maxf,tot,a[N],U[N],V[N],head[N],last[N],dep[N],cur[N];

struct edge
{
	int next,to,flow;
}e[N*10];

void add(int from,int to,int flow)
{
	e[++tot]=(edge){head[from],to,flow};
	head[from]=tot;
	swap(from,to);
	e[++tot]=(edge){head[from],to,0};
	head[from]=tot;
}

bool bfs()
{
	memset(dep,0x3f3f3f3f,sizeof(dep));
	memcpy(cur,head,sizeof(cur));
	queue<int> q;
	q.push(S); dep[S]=0;
	while (q.size())
	{
		int u=q.front(); q.pop();
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (e[i].flow && dep[v]>dep[u]+1)
			{
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]<Inf;
}

int dfs(int x,int flow)
{
	if (x==T) return flow;
	int used=0,res;
	for (int i=cur[x];~i;i=e[i].next)
	{
		int v=e[i].to; cur[x]=i;
		if (e[i].flow && dep[v]==dep[x]+1)
		{
			res=dfs(v,min(e[i].flow,flow-used));
			used+=res;
			e[i].flow-=res; e[i^1].flow+=res;
			if (flow==used) return used;
		}
	}
	return used;
}

void dinic()
{
	while (bfs()) maxf+=dfs(S,Inf);
}

int main()
{
	freopen("collection.in","r",stdin);
	freopen("collection.out","w",stdout);
	S=N-1; T=N-2;
	scanf("%d",&Q);
	while (Q--)
	{
		memset(head,-1,sizeof(head));
		tot=1; maxf=0;
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			last[i]=i; add(S,i,1);
		}
		for (int i=1;i<=m;i++)
		{
			scanf("%d%d",&U[i],&V[i]);
			add(last[U[i]],n+i*2-1,a[U[i]]);
			add(last[V[i]],n+i*2,a[V[i]]);
			add(n+i*2-1,n+i*2,1);
			add(n+i*2,n+i*2-1,1);
			last[U[i]]=n+i*2-1; last[V[i]]=n+i*2;
		}
		add(last[1],T,a[1]);
		dinic();
		printf("%d\n",maxf);
	}
	return 0;
}