正题
题目链接:https://www.ybtoj.com.cn/contest/117/problem/2
题目大意
nnn个人,每人有aia_iai个属于自己的物品。mmm次交换依次进行,每次xi,yix_i,y_ixi,yi两个人可以决定拿不拿自己的一个物品进行交换。
求111号人最后能拿到最多多少种物品
1≤n,m,ai≤30001\leq n,m,a_i\leq 30001≤n,m,ai≤3000
解题思路
每种物品只需要一个,所以每种物品的第一个可以视为流量,aia_iai可以视为自己的物品处的空位(自己的物品可以不视为自己的)。
xi,yix_i,y_ixi,yi的交换可以视为一条流量为111的双向边,因为依次进行所以要分成mmm层,然后每一层有交换的连边。
发现这样有很多点没有用到,去掉这些多余的,那点数就是O(n+m)O(n+m)O(n+m)级别的了
跑最大流就好了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=12100,inf=1e9;
struct node{int to,next,w;
}a[N<<2];
int T,n,m,tot,cnt,ans,s,t;
int ls[N],dep[N],p[N],w[N];
queue<int> q;
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;return;
}
bool bfs(){memset(dep,0,sizeof(dep));dep[s]=1;while(!q.empty())q.pop();q.push(s);while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[y]||!a[i].w)continue;dep[y]=dep[x]+1;if(y==t)return 1;q.push(y);} }return 0;
}
int dinic(int x,int flow){if(x==t)return flow;int rest=0,k;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[x]+1!=dep[y]||!a[i].w)continue;rest+=(k=dinic(y,min(a[i].w,flow-rest)));a[i].w-=k;a[i^1].w+=k;if(rest==flow)return rest;}if(!rest)dep[x]=0;return rest;
}
int main()
{freopen("collection.in","r",stdin);freopen("collection.out","w",stdout);scanf("%d",&T);while(T--){tot=0;memset(ls,0,sizeof(ls));scanf("%d%d",&n,&m);s=tot=1;t=cnt=2;ans=0;for(int i=1;i<=n;i++){p[i]=++cnt;scanf("%d",&w[i]);addl(s,p[i],1);}for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);++cnt;addl(p[x],cnt,w[x]);p[x]=cnt;++cnt;addl(p[y],cnt,w[y]);p[y]=cnt;addl(p[x],p[y],1);addl(p[y],p[x],1);}addl(p[1],t,inf);while(bfs())ans+=dinic(s,inf);printf("%d\n",ans);}return 0;
}