显然可以费用流来做,具体建图如下——
点集:源点,汇点,左边$n$个工人,右边$n$个设备
边集:源点向第$i$个工人连$(1,a_{i})$的边,第$i$个设备向汇点连$(1,b_{i})$的边,工人向可用的设备连$(1,0)$的边
跑最小费用最大流,流量为$i$时的费用即为$i$时的答案
但注意到要跑$n$次spfa,每一次的最坏复杂度为$o(n^{3})$,显然无法通过
实际上,spfa即找到$x$和$y$,使得第$x$个工人和第$y$个设备都未被使用过,且第$x$个工人能流到第$y$个设备,在此基础上最大化$a_{x}+b_{y}$,那么不妨手动来实现此功能
考虑按照$a_{i}$从大到小枚举左边未被使用过的的工人,并递归其能流到的点,求出其中设备$b_{i}$的最大值
注意到当一个点已经被访问过,显然再访问一定不优于之前,单次复杂度即降为$o(n^{2})$
每一个点只会被访问一次,因此复杂度瓶颈在于遍历边集,使用bfs来代替dfs,并对点分类讨论:
1.对于工人,其几乎到所有设备都有边,缺的仅有$o(m)$条限制和$o(n)$条之前流过的边,因此只需要遍历所有当前未被访问的点,并搜索其中可以访问的点
注意到一个未访问的点被遍历,要么被访问,要么属于$o(n+m)$条边之一,因此总复杂度即$o(m)$
2.对于设备,其出边只有$o(n)$条之前流过的边的反向边,暴力遍历边集即可,总复杂度即$o(n)$
由此,单次复杂度降为$o(m)$,总复杂度即$o(nm)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 4005 4 #define ll long long 5 queue<int>q; 6 vector<int>v[N]; 7 int t,n,m,x,y,a[N],b[N],id[N],visa[N],visb[N],vis[N],nex[N],pre[N<<1],mx[N],e[N][N]; 8 ll ans; 9 bool cmp(int x,int y){ 10 return a[x]>a[y]; 11 } 12 int bfs(int k){ 13 int s=0; 14 q.push(k); 15 vis[k]=1; 16 while (!q.empty()){ 17 int k=q.front(); 18 q.pop(); 19 if (k<=n){ 20 for(int i=nex[0],j=0;i<=n;j=i,i=nex[i]) 21 if (e[k][i]){ 22 pre[i+n]=k; 23 q.push(i+n); 24 nex[j]=nex[i],i=j; 25 } 26 } 27 else{ 28 k-=n; 29 if ((!visb[k])&&(b[s]<b[k]))s=k; 30 for(int i=0;i<v[k].size();i++) 31 if (!vis[v[k][i]]){ 32 pre[v[k][i]]=k+n; 33 q.push(v[k][i]); 34 vis[v[k][i]]=1; 35 } 36 } 37 } 38 return s; 39 } 40 int calc(){ 41 int ans=0; 42 for(int i=1;i<=n;i++)vis[i]=0; 43 for(int i=0;i<=n;i++)nex[i]=i+1; 44 for(int i=1;i<=(n<<1);i++)pre[i]=0; 45 for(int i=1;i<=n;i++){ 46 mx[i]=0; 47 if ((!visa[id[i]])&&(!vis[id[i]])){ 48 mx[i]=bfs(id[i]); 49 if (mx[i])ans=max(ans,a[id[i]]+b[mx[i]]); 50 } 51 } 52 if (!ans)return 0; 53 for(int i=1;i<=n;i++) 54 if ((mx[i])&&(a[id[i]]+b[mx[i]]==ans)){ 55 visa[id[i]]=1,visb[mx[i]]=1; 56 for(int lst=mx[i]+n,j=pre[lst];j;lst=j,j=pre[j]){ 57 if (j<=n){ 58 e[j][lst-n]=0; 59 v[lst-n].push_back(j); 60 } 61 else{ 62 e[lst][j-n]=1; 63 for(int k=0;k<v[j-n].size();k++) 64 if (v[j-n][k]==lst){ 65 v[j-n].erase(v[j-n].begin()+k); 66 break; 67 } 68 } 69 } 70 break; 71 } 72 return ans; 73 } 74 int main(){ 75 scanf("%d",&t); 76 while (t--){ 77 scanf("%d%d",&n,&m); 78 ans=0; 79 for(int i=1;i<=n;i++){ 80 visa[i]=visb[i]=0; 81 v[i].clear(); 82 } 83 for(int i=1;i<=n;i++) 84 for(int j=1;j<=n;j++)e[i][j]=1; 85 for(int i=1;i<=n;i++){ 86 scanf("%d",&a[i]); 87 id[i]=i; 88 } 89 sort(id+1,id+n+1,cmp); 90 for(int i=1;i<=n;i++)scanf("%d",&b[i]); 91 for(int i=1;i<=m;i++){ 92 scanf("%d%d",&x,&y); 93 e[x][y]=0; 94 } 95 for(int i=1;i<=n;i++){ 96 int s=calc(); 97 if (!s){ 98 for(;i<=n;i++)printf("-1 "); 99 break; 100 } 101 ans+=s; 102 printf("%lld ",ans); 103 } 104 } 105 return 0; 106 }