题目链接

http://uoj.ac/contest/47/problem/455

题解

模拟费用流,一个非常神奇的东西。

本题即为WC2019 laofu的讲课中的Problem 8,经典的老鼠进洞模型,洞有容量和额外权值。

这道题的Subtask 4,5,6,7分别对应着老鼠进洞的最基础模型、洞有额外权值、洞有容量、洞有容量和额外权值四个变形。

让我们从最简单的开始各个击破。

Subtask 4

注: 本部分配合WC2019课件里的代码图理解效果更佳。

数轴上有\(n\)只老鼠(坐标\(x_i\))和\(m\)个洞(坐标\(y_i\)),每个老鼠必须进一个洞,每个洞至多进一只老鼠,最小化距离和。

假设洞\(i\)匹配了老鼠\(j\), 则若\(y_i<x_j\)代价为\(x_j+(-y_i)\), 否则为\((-x_j)+y_i\),不妨拆成\(i,j\)两部分分别计算。

使用可撤销贪心的策略。

维护一个老鼠堆和一个洞堆,从左到右扫描。

若当前扫到一只老鼠\(i\),那么先随便匹配一个目前空闲的洞(不妨假设负无穷远处有无穷多个洞),从洞堆中取出一个元素\(j\)匹配,其代价为\(x_i+j\)

考虑当后面插入洞的时候允许老鼠反悔(像极了费用流的反向边)而匹配新的洞,那么如果一个老鼠反悔而匹配了右面的新洞\(k\), 那么代价的增量为\((y_k-x_i)-(x_i+j)=y_k+(-2x_i-j)\), 所以往老鼠堆中插入元素\(-2x_i-j\).

若当前扫到一个洞\(j\),考虑是否有老鼠要反悔,从老鼠堆中取出一个元素\(i\), 若反悔的代价\(y_j+i>0\)那么让这个老鼠堆中的老鼠反悔,同时往洞堆中加入元素\(-2y_j-i\),表示后面的老鼠再匹配这个洞的代价;否则只往洞堆中插入元素\(-y_j\).

时间复杂度\(O(n\log n)\).

Subtask 5

洞有额外权值\(w\),怎么办呢?

考虑这样和原来有什么区别: 原来只有老鼠会反悔(因为不会有舍近求远故意匹配较远洞的情况),但是现在可能出现了!

解决办法:让洞和洞匹配,洞也可以反悔。

插入洞\(i\)时,除了往洞堆中插入元素之外,往老鼠堆也插入元素,表示该洞反悔的代价。插入的元素是\(-y_i-w_i\), 相当于用新洞\(j\)替代该洞会少这么多的代价再加上\(y_j+w_j\).

坑:WC课件中那个“老鼠和洞同时反悔不优”目前没看明白。

Subtask 6

洞有容量。

有一个神仙做法,见WC课件或UOJ题解。

正常做法: 往堆里存一个pair, 记录价值和剩余容量。

每次增广会使一个往左走的老鼠改为往右走,因此复杂度正确。

Subtask 7

洞有容量,也有附加权值,显然Subtask 6的做法时间复杂度错误。

但是我们发现,如果两个老鼠\(i,j\)分别匹配洞\(k\)且\(x_i<x_j<y_k\), 那么这两种情况下往洞堆中丢的元素是一样的,都是\(-y_k-w_k\).

所以我们可以“批量加入、批量增广”,这样复杂度就得到了保证。

其实这个东西就已经非常像费用流增广的过程了。

费用流的一条重要性质是: 如果不是每次选全局最短路,而是按另一顺序对和源点相连的边必须连的点进行增广,那么也是正确的。

建议结合代码以获取更好理解。

代码

代码写出来发现和费用流神相似!

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<queue>
#include<algorithm>
#define llong long long
using namespace std; const int N = 1e5;
const llong INF = 1000000000000ll;
struct Node
{
int w; llong c;
Node() {}
Node(int _w,llong _c) {w = _w,c = _c;}
bool operator <(const Node &arg) const {return c>arg.c;}
};
struct Element
{
llong x,w,c;
} a[N+3],b[N+3];
priority_queue<Node> mouse,hole;
int n,m;
llong ans; void insertmouse(int x)
{
llong ret = INF;
if(!hole.empty())
{
Node tmp = hole.top();
ret = tmp.c+x;
hole.pop(); if(tmp.w-1>0) {hole.push(Node(tmp.w-1,tmp.c));}
}
ans += ret;
mouse.push(Node(1,-x-ret));
} void inserthole(int x,int w,llong c)
{
int rst = w;
while(!mouse.empty() && rst>0)
{
Node tmp = mouse.top();
llong val = x+c+tmp.c;
if(val>=0) break;
int flow = min(rst,tmp.w);
ans += val*flow; rst -= flow;
hole.push(Node(flow,-val-x+c));
mouse.pop(); if(tmp.w-flow>0) {mouse.push(Node(tmp.w-flow,tmp.c));}
}
if(rst>0) {hole.push(Node(rst,-x+c));}
if(w-rst>0) {mouse.push(Node(w-rst,-x-c));}
} int main()
{
scanf("%d%d",&n,&m); llong sum = 0ll;
for(int i=1; i<=n; i++) {scanf("%lld",&a[i].x);}
for(int i=1; i<=m; i++) {scanf("%lld%lld%lld",&b[i].x,&b[i].c,&b[i].w); sum += b[i].w;}
if(sum<n) {printf("-1"); return 0;}
int i = 1,j = 1;
while(i<=n||j<=m)
{
if(i<=n && (j>m||a[i].x<b[j].x)) {insertmouse(a[i].x); i++;}
else {inserthole(b[j].x,b[j].w,b[j].c); j++;}
}
printf("%lld\n",ans);
return 0;
}

UOJ #455 [UER #8]雪灾与外卖 (贪心、模拟费用流)的更多相关文章

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

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

  2. luogu P5470 [NOI2019]序列 dp 贪心 费用流 模拟费用流

    LINK:序列 考虑前20分 容易想到爆搜. 考虑dp 容易设\(f_{i,j,k,l}\)表示前i个位置 选了j对 且此时A选择了k个 B选择了l个的最大值.期望得分28. code //#incl ...

  3. 贪心(模拟费用流):NOIP2011 观光公交

    [问题描述] 风景迷人的小城Y 市,拥有n 个美丽的景点.由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务.观光公交车在第0 分钟出现在1号景点,随后依次前往2. ...

  4. BZOJ4977[Lydsy1708月赛]跳伞求生——贪心+堆+模拟费用流

    题目链接: 跳伞求生 可以将题目转化成数轴上有$n$个人和$m$个房子,坐标分别为$a_{i}$和$b_{i}$,每个人可以进一个他左边的房子,每个房子只能进一个人.每个房子有一个收益$c_{i}$, ...

  5. 模拟费用流 & 可撤销贪心

    1. CF730I Olympiad in Programming and Sports 大意: $n$个人, 第$i$个人编程能力$a_i$, 运动能力$b_i$, 要选出$p$个组成编程队, $s ...

  6. 题解-UOJ 455雪灾与外卖

    Problem \(\mathrm{UOJ~455}\) 题意概要:一根数轴上有 \(n\) 只老鼠与 \(m\) 个洞,每个洞有费用与容量限制,要求每只老鼠要进一个洞且每个洞的老鼠不超过自身的容量限 ...

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

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

  8. 贪心+模拟 Codeforces Round #288 (Div. 2) C. Anya and Ghosts

    题目传送门 /* 贪心 + 模拟:首先,如果蜡烛的燃烧时间小于最少需要点燃的蜡烛数一定是-1(蜡烛是1秒点一支), num[g[i]]记录每个鬼访问时已点燃的蜡烛数,若不够,tmp为还需要的蜡烛数, ...

  9. UVALive 4863 Balloons 贪心/费用流

    There will be several test cases in the input. Each test case will begin with a line with three inte ...

随机推荐

  1. java-阿里云发送短信

    <!--aliyun短信sdk--> <dependency> <groupId>com.aliyun</groupId> <artifactId ...

  2. Java设计模式七种写法

    懒汉模式-线程不安全 public class Singleton { private static Singleton instance; private Singleton (){ } publi ...

  3. IntelliJ IDEA 复杂的重构技巧

    IntelliJ IDEA 复杂的重构技巧(二) 转载 上次我说了一些 "复杂的重构技巧" ,讲的是一些使用 IntelliJ 的简单功能实现复杂的重构需求的技巧. 看到大家的反响 ...

  4. C# 面向对象2 (类的语法)

    1.类 语法: [public] class 类名 { 字段; 属性; 方法; } **类名首字母必须大写 2.创建对象 创建这个类的对象过程称之为类的实例化,关键字:new this:表示当前这个类 ...

  5. JVM - Java虚拟机规范官方文档

    Java虚拟机规范官方文档    

  6. java读取blob全身乱码

    一.BLOB操作 .入库 ()JDBC方式 //通过JDBC获得数据库连接 Class.forName("oracle.jdbc.driver.OracleDriver"); Co ...

  7. webpack 四个核心概念

    webpack 是当下最热门的前端资源模块化和打包工具.它可以将许多松散的模块(如 CommonJs 模块. AMD 模块. ES6 模块.CSS.图片. JSON.Coffeescript. LES ...

  8. 二,kubernetes集群的安装初始化

    目录 部署 集群架构示意图 部署环境 kubernetes集群部署步骤 基础环境 基础配置 安装基础组件 配置yum源 安装组件 初始化 master 设置docker和kubelet为自启动(nod ...

  9. Spring笔记之配置数据源

    任何DAO访问数据库,最开始都需要配置数据源,数据源中定义了访问数据库的常用配置.有了数据源才能创建数据模板,然后把数据模板注入到DAO中,通过调用数据访问模板中的相应方法来对数据库进行相关操作. 常 ...

  10. 会了docker你又多了一个谈资(下)

    上篇文章介绍了docker 基本使用及安装([跳转☞会了docker你又多了一个谈资(上)],这篇重点说明下docker使用技巧. 问题1怎么用docker搭建多台服务器? 只需要 docker ru ...