显然可以费用流来做,具体建图如下——

点集:源点,汇点,左边$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. 1 #include<bits/stdc++.h>
  2. 2 using namespace std;
  3. 3 #define N 4005
  4. 4 #define ll long long
  5. 5 queue<int>q;
  6. 6 vector<int>v[N];
  7. 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. 8 ll ans;
  9. 9 bool cmp(int x,int y){
  10. 10 return a[x]>a[y];
  11. 11 }
  12. 12 int bfs(int k){
  13. 13 int s=0;
  14. 14 q.push(k);
  15. 15 vis[k]=1;
  16. 16 while (!q.empty()){
  17. 17 int k=q.front();
  18. 18 q.pop();
  19. 19 if (k<=n){
  20. 20 for(int i=nex[0],j=0;i<=n;j=i,i=nex[i])
  21. 21 if (e[k][i]){
  22. 22 pre[i+n]=k;
  23. 23 q.push(i+n);
  24. 24 nex[j]=nex[i],i=j;
  25. 25 }
  26. 26 }
  27. 27 else{
  28. 28 k-=n;
  29. 29 if ((!visb[k])&&(b[s]<b[k]))s=k;
  30. 30 for(int i=0;i<v[k].size();i++)
  31. 31 if (!vis[v[k][i]]){
  32. 32 pre[v[k][i]]=k+n;
  33. 33 q.push(v[k][i]);
  34. 34 vis[v[k][i]]=1;
  35. 35 }
  36. 36 }
  37. 37 }
  38. 38 return s;
  39. 39 }
  40. 40 int calc(){
  41. 41 int ans=0;
  42. 42 for(int i=1;i<=n;i++)vis[i]=0;
  43. 43 for(int i=0;i<=n;i++)nex[i]=i+1;
  44. 44 for(int i=1;i<=(n<<1);i++)pre[i]=0;
  45. 45 for(int i=1;i<=n;i++){
  46. 46 mx[i]=0;
  47. 47 if ((!visa[id[i]])&&(!vis[id[i]])){
  48. 48 mx[i]=bfs(id[i]);
  49. 49 if (mx[i])ans=max(ans,a[id[i]]+b[mx[i]]);
  50. 50 }
  51. 51 }
  52. 52 if (!ans)return 0;
  53. 53 for(int i=1;i<=n;i++)
  54. 54 if ((mx[i])&&(a[id[i]]+b[mx[i]]==ans)){
  55. 55 visa[id[i]]=1,visb[mx[i]]=1;
  56. 56 for(int lst=mx[i]+n,j=pre[lst];j;lst=j,j=pre[j]){
  57. 57 if (j<=n){
  58. 58 e[j][lst-n]=0;
  59. 59 v[lst-n].push_back(j);
  60. 60 }
  61. 61 else{
  62. 62 e[lst][j-n]=1;
  63. 63 for(int k=0;k<v[j-n].size();k++)
  64. 64 if (v[j-n][k]==lst){
  65. 65 v[j-n].erase(v[j-n].begin()+k);
  66. 66 break;
  67. 67 }
  68. 68 }
  69. 69 }
  70. 70 break;
  71. 71 }
  72. 72 return ans;
  73. 73 }
  74. 74 int main(){
  75. 75 scanf("%d",&t);
  76. 76 while (t--){
  77. 77 scanf("%d%d",&n,&m);
  78. 78 ans=0;
  79. 79 for(int i=1;i<=n;i++){
  80. 80 visa[i]=visb[i]=0;
  81. 81 v[i].clear();
  82. 82 }
  83. 83 for(int i=1;i<=n;i++)
  84. 84 for(int j=1;j<=n;j++)e[i][j]=1;
  85. 85 for(int i=1;i<=n;i++){
  86. 86 scanf("%d",&a[i]);
  87. 87 id[i]=i;
  88. 88 }
  89. 89 sort(id+1,id+n+1,cmp);
  90. 90 for(int i=1;i<=n;i++)scanf("%d",&b[i]);
  91. 91 for(int i=1;i<=m;i++){
  92. 92 scanf("%d%d",&x,&y);
  93. 93 e[x][y]=0;
  94. 94 }
  95. 95 for(int i=1;i<=n;i++){
  96. 96 int s=calc();
  97. 97 if (!s){
  98. 98 for(;i<=n;i++)printf("-1\n");
  99. 99 break;
  100. 100 }
  101. 101 ans+=s;
  102. 102 printf("%lld\n",ans);
  103. 103 }
  104. 104 }
  105. 105 return 0;
  106. 106 }

[hdu6978]New Equipments II的更多相关文章

  1. Leetcode 笔记 113 - Path Sum II

    题目链接:Path Sum II | LeetCode OJ Given a binary tree and a sum, find all root-to-leaf paths where each ...

  2. Leetcode 笔记 117 - Populating Next Right Pointers in Each Node II

    题目链接:Populating Next Right Pointers in Each Node II | LeetCode OJ Follow up for problem "Popula ...

  3. 函数式Android编程(II):Kotlin语言的集合操作

    原文标题:Functional Android (II): Collection operations in Kotlin 原文链接:http://antonioleiva.com/collectio ...

  4. 统计分析中Type I Error与Type II Error的区别

    统计分析中Type I Error与Type II Error的区别 在统计分析中,经常提到Type I Error和Type II Error.他们的基本概念是什么?有什么区别? 下面的表格显示 b ...

  5. hdu1032 Train Problem II (卡特兰数)

    题意: 给你一个数n,表示有n辆火车,编号从1到n,入站,问你有多少种出站的可能.    (题于文末) 知识点: ps:百度百科的卡特兰数讲的不错,注意看其参考的博客. 卡特兰数(Catalan):前 ...

  6. [LeetCode] Guess Number Higher or Lower II 猜数字大小之二

    We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...

  7. [LeetCode] Number of Islands II 岛屿的数量之二

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  8. [LeetCode] Palindrome Permutation II 回文全排列之二

    Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empt ...

  9. [LeetCode] Permutations II 全排列之二

    Given a collection of numbers that might contain duplicates, return all possible unique permutations ...

随机推荐

  1. python中return的返回和执行

    1 打印函数名和打印函数的执行过程的区别 例子1.1 def a(): print(111) print(a) # 打印a函数的内存地址,不会对a函数有影响,a函数不会执行 print(a()) # ...

  2. 2021.5.22 vj补题

    A - Marks CodeForces - 152A 题意:给出一个学生人数n,每个学生的m个学科成绩(成绩从1到9)没有空格排列给出.在每科中都有成绩最好的人或者并列,求出最好成绩的人数 思路:求 ...

  3. c++-string类--insert函数

    string &insert(int p0, const char *s);--在p0位置插入字符串s string &insert(int p0, const char *s, in ...

  4. 后缀自动机(SAM)奶妈式教程

    后缀自动机(SAM) 为了方便,我们做出如下约定: "后缀自动机" (Suffix Automaton) 在后文中简称为 SAM . 记 \(|S|\) 为字符串 \(S\) 的长 ...

  5. Java(8)详解Random使用

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201556.html 博客主页:https://www.cnblogs.com/testero ...

  6. CAD网页Web端显示开发为什么要以WebGIS的思路来开发?

    背景 在之前的博文CAD图DWG解析WebGIS可视化技术分析总结中讲解了如何把CAD的DWG格式的图纸Web可视化的方案.博文发布后,受到不少同行们的关注,也有不少咨询一些专业问题,其中大家可能疑惑 ...

  7. Google Style Guides

    Google Style Guides Google Style Guides Google 开源项目风格指南 (zh-google-styleguide.readthedocs.io)

  8. python解释器和Pycharm编辑器安装使用完整详细教程

    一.官网下载或软件管家公众号下载 二.安装Python解释器 1.选择自定义安装并添加到环境变量 2.检验Python是否安装成功 三.安装pycharm编辑器 1.点击安装,修改安装路径,建议安装C ...

  9. airtest常用指令

    airtest 操作adb命令   常用adb 1)对特定设备执行adb指令 dev = connect_device("Android:///device1") dev.shel ...

  10. elasticsearch的索引重建

    我们知道es在字段的mapping建立后就不可再次修改mapping的值.在我们实际的情况下有些时候就是需要修改mapping的值,解决方案就是重新构建索引数据. 方式一 : 使用索引别名,创建另外一 ...