Problem

\(\mathrm{UOJ~455}\)

题意概要:一根数轴上有 \(n\) 只老鼠与 \(m\) 个洞,每个洞有费用与容量限制,要求每只老鼠要进一个洞且每个洞的老鼠不超过自身的容量限制,定义一种方案的费用为所有老鼠移动距离之和加上所有老鼠进的洞费用之和(若一个洞进了 \(k\) 只老鼠,则费用需要计算 \(k\) 次)

\(n,m\leq 10^5\)

Solution

冬令营时掉线了,只记得这题被大家把好评刷上去了

这题是所谓模拟费用流问题。我理解的模拟费用流其实等价于处理“反悔”也即反向弧,因为费用流问题是使用 最短路(贪心) 加 反向弧(反悔) 解决的,所以利用该种思想,在平时题目中,若贪心有瑕疵,可以考虑维护后悔操作。相似的题有bzoj4977跳伞逃生

将所有老鼠和洞按照坐标排序先

bzoj4977相当于这题中老鼠只能往左走,即只能往前匹配,这样可以从左往右维护一个堆解决贪心。

而这题中老鼠和洞都可能往右匹配,但是依照上述“贪心+后悔”的思想,考虑先让老鼠和洞往左匹配。

维护老鼠和洞两个堆,分别设为 \(A,B\);明确老鼠进洞的代价是两个坐标中靠右的减去靠左的。

下面开始讨论:

若当前为老鼠,且其坐标为 \(x\),若从 \(B\) 中取得的最优解为 \(v\),则对答案贡献 \(x+v\)。这样的话只考虑了向左的匹配,但实际有可能向右匹配(反悔),考虑将这次匹配的贡献消除,且将此点设定为左端,则需向 \(A\) 中加入 \(-(v+x)-x\)(前者将当前贡献消除,后者假定当前为左端点需减去)

若当前为餐厅,且其坐标为 \(y\),费用为 \(w\),若从 \(A\) 中取得的最优解为 \(v\),对答案贡献 \(w+y+v\),与上头类似,为了维护反悔操作需在 \(B\) 中加入 \(-(w+y+v)-y\)

打完后发现样例中有俩过不去,问了大神们才知道我少考虑了一种情况:若当前顺序为 鼠、洞、洞,我的方法一旦匹配了第一个洞和鼠后,不能考虑鼠反悔去匹配第二个洞的情况

那么就再加入一种反悔操作:往 \(A\) 中加入 \(-w-y\) 即可。具体地说,假设鼠价值 \(v\),两个洞的费用与坐标分别为 \(w_1,w_2,y_1,y_2\),那从第一种情况反悔到第二种情况的贡献为 \((w_2+y_2+v)-(w_1+y_1+v)=w_2+y_2-w_1-y_1\),由于 \(w_2+y_2\) 会在后面统计到,所以只需往堆中加入 \(-w_1-y_1\) 即可。至于为什么不需要对 洞、鼠、鼠 的情况做反悔,因为所有老鼠都是一样的,不可能存在舍近求远的情况

还有一点,由于所有老鼠都要求进洞,若最左侧的老鼠在最左侧的洞的左边,则这只老鼠有可能不会进洞,所以可以在最最最左侧加入一个费用无穷大的洞,这样左侧的老鼠匹配它后立马就会被替换掉,进而所有老鼠都会被匹配(在有解的情况下)

至于如何考虑每个洞的容量限制,可以将每个洞作为一个整体放进堆里,记录下剩余使用次数,作为反悔可以每次分裂一个出来

虽然思想比较简单,但是由于用到多次反悔操作(甚至有反悔后的反悔),所以代码难以调试,建议静态差错,最好一遍过

Code

代码中 \(P\) 表示老鼠,\(R\) 表示洞

  1. //uoj-455
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. typedef long long ll;
  5. typedef pair <ll,int> pr;
  6. #define mp make_pair
  7. template <typename _tp> inline void read(_tp&x){
  8. char c11=getchar(),ob=0;x=0;
  9. while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
  10. while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
  11. }
  12. const int N = 401000;
  13. const ll Inf = 2e17;
  14. int x[N],n,m;
  15. struct node{
  16. int y,w,c;
  17. inline void in(){read(y),read(w),read(c);}
  18. friend inline bool operator < (const node&A,const node&B) {return A.y < B.y;}
  19. }p[N];
  20. struct Heap{
  21. pr h[N]; int tp;
  22. inline bool empty() {return !tp;}
  23. inline pr top() {return h[1];}
  24. inline void push(pr x) {h[++tp] = x; push_heap(h+1,h+tp+1,greater <pr> ());}
  25. inline void pop() {pop_heap(h+1,h+tp+1,greater <pr> ()); --tp;}
  26. }R,P;
  27. int main(){
  28. read(n),read(m);
  29. ll tot = 0;
  30. for(int i=1;i<=n;++i) read(x[i]); sort(x+1,x+n+1);
  31. for(int i=1;i<=m;++i) p[i].in(), tot += p[i].c; sort(p+1,p+m+1);
  32. if(tot < n) {puts("-1"); return 0;}
  33. R.push(mp(Inf,2e8));
  34. x[n+1] = 2e9;
  35. ll Ans = 0ll;
  36. for(int i=1,j=1;i<=n or j<=m;) {
  37. while(p[j].y <= x[i] and j <= m){
  38. int usc = 0;
  39. while(!P.empty() and p[j].c){
  40. pr nw = P.top();
  41. ll val = p[j].w + p[j].y + nw.first;
  42. if(val > 0) break; P.pop();
  43. int ct = min(nw.second, p[j].c);
  44. Ans += (ll)ct * val;
  45. R.push(mp(-nw.first - 2*p[j].y, ct));
  46. p[j].c -= ct, usc += ct, nw.second -= ct;
  47. if(nw.second) P.push(nw);
  48. }
  49. if(usc) P.push(mp(-p[j].w - p[j].y, usc));
  50. if(p[j].c) R.push(mp(p[j].w - p[j].y, p[j].c));
  51. ++j;
  52. }
  53. if(i > n) continue;
  54. pr nw = R.top(); R.pop();
  55. Ans += nw.first + x[i];
  56. P.push(mp(-(nw.first + x[i]) - x[i], 1));
  57. --nw.second; if(nw.second) R.push(nw);
  58. ++i;
  59. }
  60. printf("%lld\n",Ans);
  61. return 0;
  62. }

题解-UOJ 455雪灾与外卖的更多相关文章

  1. [UOJ455][UER #8]雪灾与外卖——堆+模拟费用流

    题目链接: [UOJ455]雪灾与外卖 题目描述:有$n$个送餐员(坐标为$x_{i}$)及$m$个餐厅(坐标为$y_{i}$,权值为$w_{i}$),每个送餐员需要前往一个餐厅,每个餐厅只能容纳$c ...

  2. UOJ #455 [UER #8]雪灾与外卖 (贪心、模拟费用流)

    题目链接 http://uoj.ac/contest/47/problem/455 题解 模拟费用流,一个非常神奇的东西. 本题即为WC2019 laofu的讲课中的Problem 8,经典的老鼠进洞 ...

  3. uoj455 【UER #8】雪灾与外卖

    http://uoj.ac/problem/455 题解: https://blog.csdn.net/litble/article/details/88410435 https://www.mina ...

  4. 【胡策篇】题解 (UOJ 192 + CF938G + SPOJ DIVCNT2)

    和泉纱雾与烟花大会 题目来源: UOJ 192 最强跳蚤 (只改了数据范围) 官方题解: 在这里哦~(说的很详细了 我都没啥好说的了) 题目大意: 求树上各边权乘积是完全平方数的路径数量. 这种从\( ...

  5. 【UER #8】雪灾与外卖

    题解: 这个东西的模型是个费用流 但是直接跑费用流能拿到5分的高分 $(nm)*(nm)*log{nm}$ 考虑优化一下建图 我们可以不用对每个店和人都连边 而是对人和店都连一条链 然后对每个人连店刚 ...

  6. UOJ 12 猜数 数学题

    [UER #1]猜数 这一天,小Y.小D.小C正在愉快地玩耍. 小Y是个数学家,他一拍脑袋冒出了一个神奇的完全平方数 n. 小D是个机灵鬼,很快从小Y嘴里套出了 n 的值.然后在脑内把 n 写成了 a ...

  7. 【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升

    [UOJ#278][UTR #2]题目排列顺序 试题描述 “又要出题了.” 宇宙出题中心主任 —— 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI. 这场比赛有 n 道题,吉米多出题斯基需要决定这 ...

  8. NOIWC2019 冬眠记

    冬眠 由于THUWC考太差了没啥心情做事…… Day -1 报到日前一天晚上去看了看宿舍表,发现周围全是集训队,隔壁就是栋爷.高队和lca,再隔壁是zzq和wxh……吓傻了(本校buff这么好吗) D ...

  9. ZOJ - 3469 Food Delivery (区间dp)

    When we are focusing on solving problems, we usually prefer to stay in front of computers rather tha ...

随机推荐

  1. React Native Flexbox & CSS3 Flexbox

    React Native Flexbox & CSS3 Flexbox https://facebook.github.io/react-native/docs/flexbox/ https: ...

  2. python的排序方式

    """ 冒泡排序: 冒泡排序的思想: 每次比较两个相邻的元素, 如果他们的顺序错误就把他们交换位置 比如有五个数: 12, 35, 99, 18, 76, 从大到小排序, ...

  3. 【CF1063F】String Journey 哈希

    题目大意 给你一个字符串 \(s\),让你找出最大的 \(k\),满足:能从 \(s\) 中选出 \(k\) 个不重叠的字符串 \(t_1,t_2,\ldots,t_k\),且 \(\forall i ...

  4. OpenFlow Flow-Mod消息学习

    任务内容 1. 熟悉Flow-Mod消息触发场景. 2. 掌握Flow-Mod消息格式和常用字段含义. 实验原理 OpenFlow 协议支持3种消息类型:Controller-to-Switch(控制 ...

  5. 2019余姚培训游记+ZJOJD2划水记

    2019余姚培训游记 突然就想写一个... 注意:以下全是胡言乱语的自high,还有很多错别字 Day 0 来的比较早,早上就到了 上午把一本小说看完了,是一个年轻作者的处女作. 我觉得我第一本书一定 ...

  6. Ubuntu terminal colors

    Today I run ubuntu docker image on powershell and find the directory color is blue, so I want to cha ...

  7. python之路day09--函数

    s='金老板啊'print(len(s)) #内置函数 s='金老板啊'# def my_len():# i=0# for k in s:# i+=1# # print(i)# print(my_le ...

  8. IOC的理解(转载)

    转载自:https://www.zhihu.com/question/23277575/answer/169698662 要了解控制反转( Inversion of Control ), 我觉得有必要 ...

  9. pwn-格式化字符串漏洞

    原理:因为没有正确使用printf()函数 正确使用 : printf('%s',str) 不正规使用:printf(str) 控制字符串str可以爆出stack内内容从而实现任意地址读或者任意地址写 ...

  10. redisson整合spring

    转: redisson整合spring 转: 原文:http://blog.csdn.net/wang_keng/article/details/73549274 首先讲下什么是Redisson:Re ...