题解-UOJ 455雪灾与外卖

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\) 表示洞

//uoj-455
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

typedef pair <ll,int> pr;
#define mp make_pair

template <typename _tp> inline void read(_tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
}

const int N = 401000;
const ll Inf = 2e17;
int x[N],n,m;

struct node{
    int y,w,c;
    inline void in(){read(y),read(w),read(c);}
    friend inline bool operator < (const node&A,const node&B) {return A.y < B.y;}
}p[N];

struct Heap{
    pr h[N]; int tp;
    inline bool empty() {return !tp;}
    inline pr top() {return h[1];}
    inline void push(pr x) {h[++tp] = x; push_heap(h+1,h+tp+1,greater <pr> ());}
    inline void pop() {pop_heap(h+1,h+tp+1,greater <pr> ()); --tp;}
}R,P;

int main(){
    read(n),read(m);
    ll tot = 0;
    for(int i=1;i<=n;++i) read(x[i]); sort(x+1,x+n+1);
    for(int i=1;i<=m;++i) p[i].in(), tot += p[i].c; sort(p+1,p+m+1);

    if(tot < n) {puts("-1"); return 0;}

    R.push(mp(Inf,2e8));
    x[n+1] = 2e9;

    ll Ans = 0ll;
    for(int i=1,j=1;i<=n or j<=m;) {
        while(p[j].y <= x[i] and j <= m){
            int usc = 0;
            while(!P.empty() and p[j].c){
                pr nw = P.top();
                ll val = p[j].w + p[j].y + nw.first;
                if(val > 0) break; P.pop();
                int ct = min(nw.second, p[j].c);
                Ans += (ll)ct * val;
                R.push(mp(-nw.first - 2*p[j].y, ct));
                p[j].c -= ct, usc += ct, nw.second -= ct;
                if(nw.second) P.push(nw);
            }
            if(usc) P.push(mp(-p[j].w - p[j].y, usc));
            if(p[j].c) R.push(mp(p[j].w - p[j].y, p[j].c));
            ++j;
        }
        if(i > n) continue;
        pr nw = R.top(); R.pop();
        Ans += nw.first + x[i];
        P.push(mp(-(nw.first + x[i]) - x[i], 1));
        --nw.second; if(nw.second) R.push(nw);
        ++i;
    }
    printf("%lld\n",Ans);
    return 0;
}

原文地址:https://www.cnblogs.com/penth/p/10485763.html

时间: 2024-09-30 10:34:25

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

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

题目链接: [UOJ455]雪灾与外卖 题目描述:有$n$个送餐员(坐标为$x_{i}$)及$m$个餐厅(坐标为$y_{i}$,权值为$w_{i}$),每个送餐员需要前往一个餐厅,每个餐厅只能容纳$c_{i}$个送餐员,一个送餐员去一个餐厅的代价为$|x_{i}-y_{j}|+w_{j}$,求最小代价. 首先这个题可以暴力建图跑费用流,具体做法就不说了.现在我们考虑模拟费用流的过程,也就是模拟贪心及匹配中反悔的过程. 我们对送餐员和餐厅分别开一个小根堆然后从左往右决策每个坐标位置的人或餐厅的选择

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

[UOJ#278][UTR #2]题目排列顺序 试题描述 "又要出题了." 宇宙出题中心主任 -- 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI. 这场比赛有 n 道题,吉米多出题斯基需要决定这些题目的难度,然后再在汪洋大海中寻找符合该难度的题目. 题目的难度可以用一个 1 到 n 的排列 a1,-,an 表示,其中 ai 表示第 i 道题目在这 n 道题目中是第 ai 简单的题目,即恰有 ai?1 道题目比第 i 道题目简单. 经验丰富的吉米多出题斯基早就悟出了一种科学地决定难

[luogu] P4040 [AHOI2014/JSOI2014]宅男计划(贪心)

P4040 [AHOI2014/JSOI2014]宅男计划 题目背景 自从迷上了拼图,JYY就变成了个彻底的宅男.为了解决温饱问题,JYY不得不依靠叫外卖来维持生计. 题目描述 外卖店一共有N种食物,分别有1到N编号.第i种食物有固定的价钱Pi和保质期Si.第i种食物会在Si天后过期.JYY是不会吃过期食物的. 比如JYY如果今天点了一份保质期为1天的食物,那么JYY必须在今天或者明天把这个食物吃掉,否则这个食物就再也不能吃了.保质期可以为0天,这样这份食物就必须在购买当天吃掉. JYY现在有M

模拟流的问题小结

模拟流的问题小结 模拟流的问题一般都是通过建出流的模型,观察流的性质然后用数据结构高效模拟流. Codeforces 280 D. k-Maximum Subsequence Sum 有一个长度为 \(n\) 的序列,单点修改,维护在这个序列中选取至多 \(k\) 个互不相交子序列的权值和最大值,\(n \leq 10^5, k \leq 20\). 拆点后建出费用流模型 \[ (S, S',k, 0) \(S', i, 1, 0) \(i+n,T,1,0) \(i,i+n,1,a_i) \(i

codevs2800送外卖题解

题目描述 Description 有一个送外卖的,他手上有n份订单,他要把n份东西,分别送达n个不同的客户的手上.n个不同的客户分别在1~n个编号的城市中.送外卖的从0号城市出发,然后n个城市都要走一次(一个城市可以走多次),最后还要回到0点(他的单位),请问最短时间是多少.现在已知任意两个城市的直接通路的时间. 输入描述 Input Description 第一行一个正整数n (1<=n<=15) 接下来是一个(n+1)*(n+1)的矩阵,矩阵中的数均为不超过10000的正整数.矩阵的i行j

【UOJ #6】AB题解

#74 破解密码 首先容易得出第i位编号为x的字母的公式: (26^n -1)*x=26*h[i]-h[i+1] 我们可以先求出(26^n -1)%mod的逆元,乘到右边去即可. 可是,这样做只有50分! (26^n -1)%mod=0的时候没有逆元!!也就是说这种情况下x为任何数都可以,而这种算法会导致全部输出a,h[]全部都是0了,可能与读入的h不符. 因此这种情况的做法是:求出h[1]用26进制表示的n位数,直接输出就是答案了.因为他满足h[1],后面的必然满足. <span style=

uoj Goodbye Dingyou Round 题解

2.14 晚上的比赛, 现在改好了四题, 还差提答. 在这补个题解 新年的xor Description 给你 \(n\) , 然后要你构造 \([L, R], L<R\) 使得区间异或和为 \(n\) Solution 做法多的是 我的方法是特判 \(n\le 4\) 的, 然后对于奇数用 \([n-3,n-1]\) , 偶数用 \([n-4, n]\) 新年的叶子 Description 一棵 \(n\le 5e5\) 的树, 每次会随机将一个原树的叶子染黑, 问白点最远距离什么时候变小 S

UOJ Easy Round #8 T1 打雪仗 题解

题目链接: [UER #8]打雪仗 第一次做通信题,写篇\(blog\)加深印象. 首先分析题目,根据数据,最坏情况下\(m\approx \frac23n\) 刚开始时想着把进制压到更高进制输出,不过实现不来放弃了. 那么把\(2n\)分成一些长度为\(3\)的区间,对于\(1,2\)个字符,直接由小\(B\)告诉小\(A\)是否需要,如果需要则小\(A\)发送字符. 对于第\(3\)个字符,无论需不需要都由小\(A\)发出. 那么显然小\(B\)的输出长度正好为\(\frac23n\). 对

UOJ Round 19 题解

虽然打得的确一般般,但是不知道为什么有心情来写( 开场--这 T3 怎么这么眼熟?然而数据范围这么大-- 写了再说,反正跑不满,说不定卡进去了( 然而不行,5e5 的大样例跑了 0.5s+-- 卡了一会感觉布星,溜了. 开始找 T1 的一堆性质,感觉找到了. 然后要找很多极小环,但是不会. 码了个 T2 的暴力就去搞 T1 的极小环了,以为能有 70,最后连那个说大不大的样例都过不去,一画图发现假了. 这时只剩下 10min,改成 20 分暴力走人了. 丢人 20+20+85=125,rk22,