传送门


首先\(\sum c\)有些大,考虑将其缩小降低难度

考虑一个贪心:第一次所有老鼠都进入其左边第一个容量未满的洞(如果左边没有就进入右边第一个未满的洞),第二次所有老鼠都进入其右边第一个容量未满的洞(如果右边没有就进入左边第一个未满的洞),我们只保留这\(2N\)个洞,答案也不会变,因为在最优情况下老鼠最多只会进入这些洞。通过这一步,我们的\(\sum c\)降低到了\(2N\)级别。

考虑将容量为\(c\)的洞拆分为\(c\)个容量为\(1\)的洞,将老鼠和洞放在一起按照\(dis\)即位置从小到大排序。然后不难想到一个\(DP\):设\(f_{i,j}\)表示已经考虑前\(i\)个物体,其中老鼠的数量减去强制选定需要容纳一只老鼠的洞的数量为\(j\)时的最小距离,转移分当前为老鼠还是洞。

但是如果直接转移很难找到优化点,考虑简化DP过程。

如果当前是一只老鼠,那么我们的转移是固定的:\(j \geq 0\)则\(f_{i,j} - dis_i \rightarrow f_{i+1,j+1}\),\(j<0\)则\(f_{i,j} + dis_i \rightarrow f_{i+1,j+1}\)

如果当前是一个洞,对于\(j>0\)的情况,因为之前的老鼠一定要往右边走,而当前的洞是右边最近的一个,所以它一定会容纳一只老鼠,也就是\(f_{i,j} + dis_i \rightarrow f_{i+1,j-1}\)

对于\(j<0\)的情况,之前有若干个洞还没有容纳老鼠,那么右边出现的老鼠一定会往左走,而这一个洞比它左边的洞相比于右边的老鼠更近,所以如果不选这一个洞,可以不选之前的某一个洞而改选这一个洞获得更优策略。这意味着当\(j<0\)是这一个洞是必须要选的,转移会是\(f_{i,j} - dis_i \rightarrow f_{i+1,j-1}\)。

那么唯独需要决策的只有\(j=0\)且当前是一个洞的情况,你可以选择这一个洞不容纳老鼠,那么会有一个特殊的转移:\(f_{i+1,0} = \min\{f_{i , 0} , f_{i , 1} + dis_i\}\)。

发现除了\(f_{i,0}\)的转移以外其他的转移都是平移数组和统一加上减去一个值的形式。我们通过两个栈维护\(DP\)数组,一个维护\(j>0\),一个维护\(j<0\),在栈外部打上整体加法标记,即可\(O(n)\)维护\(DP\)。

#include<bits/stdc++.h>
//This code is written by Itst
using namespace std; inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return f ? -a : a;
} const int MAXN = 1e5 + 7;
stack < long long > dp1 , dp2;
struct stg{
int a , b;
bool operator <(const stg t)const{
return a < t.a;
}
}now[MAXN];
int mice[MAXN] , hole[MAXN][2] , times[MAXN][2] , dp[MAXN * 3][2];
int N , M , cnt;
long long sum , tag1 , tag2 , dp0; int main(){
N = read();
M = read();
for(int i = 1 ; i <= N ; ++i)
mice[i] = read();
sort(mice + 1 , mice + N + 1);
for(int i = 1 ; i <= M ; ++i){
now[i].a = read();
now[i].b = read();
sum += now[i].b;
}
sort(now + 1 , now + M + 1);
for(int i = 1 ; i <= M ; ++i){
hole[i][0] = now[i].a;
hole[i][1] = now[i].b;
}
if(sum < N){
puts("-1");
return 0;
}
int p1 = 1 , p2 = 1;
while(p1 <= N || p2 <= M)
if(p1 <= N && (p2 > M || hole[p2][0] > mice[p1])){
++p1;
if(!dp1.empty()){
int t = dp1.top();
dp1.pop();
if(++times[t][0] != hole[t][1])
dp1.push(t);
}
else
++cnt;
}
else
if(cnt < hole[p2][1]){
times[p2][0] = cnt;
cnt = 0;
dp1.push(p2++);
}
else{
times[p2][0] = hole[p2][1];
cnt -= hole[p2++][1];
}
p1 = N;
p2 = M;
cnt = 0;
dp1 = dp2;
while(p1 || p2)
if(p1 && (!p2 || hole[p2][0] < mice[p1])){
--p1;
if(!dp1.empty()){
int t = dp1.top();
dp1.pop();
if(++times[t][1] != hole[t][1])
dp1.push(t);
}
else
++cnt;
}
else
if(cnt < hole[p2][1]){
times[p2][1] = cnt;
cnt = 0;
dp1.push(p2--);
}
else{
times[p2][1] = hole[p2][1];
cnt -= hole[p2--][1];
}
p1 = 1;
p2 = 1;
cnt = 0;
while(p1 <= N || p2 <= M)
if(p1 <= N && (p2 > M || hole[p2][0] > mice[p1]))
dp[++cnt][0] = mice[p1++];
else{
for(int i = min(times[p2][0] + times[p2][1] , hole[p2][1]) ; i ; --i){
dp[++cnt][0] = hole[p2][0];
dp[cnt][1] = 1;
--times[p2][0];
}
++p2;
}
dp1 = dp2;
for(int i = 1 ; i <= cnt ; ++i)
if(!dp[i][1]){
dp1.push(dp0 - tag1);
tag1 -= dp[i][0];
tag2 += dp[i][0];
if(!dp2.empty()){
dp0 = dp2.top() + tag2;
dp2.pop();
}
else
dp0 = 1e15;
}
else{
dp2.push(dp0 - tag2);
tag1 += dp[i][0];
tag2 -= dp[i][0];
if(!dp1.empty()){
dp0 = min(dp0 , dp1.top() + tag1);
dp1.pop();
}
}
cout << dp0;
return 0;
}

CF797F Mice and Holes 贪心、栈维护DP的更多相关文章

  1. Mice and Holes 单调队列优化dp

    Mice and Holes 单调队列优化dp n个老鼠,m个洞,告诉你他们的一维坐标和m个洞的容量限制,问最小总距离.1 ≤ n, m ≤ 5000. ​ 首先列出朴素的dp方程:\(f[i][j] ...

  2. Codeforces 797 F Mice and Holes

    http://codeforces.com/problemset/problem/797/F F. Mice and Holes time limit per test             1.5 ...

  3. [置顶] hdu 4699 2个栈维护 or 伸展树

    hdu 4699  Editor 题意:对一个数列进行操作,光标位置后面插入一个权值为x的数,删除光标前的那个数,光标左移一位,光标右移一位,求到k位置的最大的前缀和.. 注意这里的k是在光标之前的, ...

  4. 【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】

    比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; ...

  5. AC日记——Mice and Holes codeforces 797f

    797F - Mice and Holes 思路: XXYXX: 代码: #include <cmath> #include <cstdio> #include <cst ...

  6. Mice and Holes CodeForces - 797F

    Mice and Holes CodeForces - 797F 题意:有n只老鼠和m个洞,都在一个数轴上,老鼠坐标为x[1],...,x[n],洞的坐标为p[1],...,p[m],每个洞能容纳的老 ...

  7. UVALive - 4097:Yungom(逼近 贪心)(DP)

    pro:有D个字母,每个字母有自己的权值,现状需要用它们拼出N个单词,使得这些单词互相不为另外一个的前缀. 且单词的权值和最小.D<=200; N<=200; sol:如果建立字典树,那个 ...

  8. csp-s模拟测试50(9.22)「施工(单调栈优化DP)」·「蔬菜(二维莫队???)」·「联盟(树上直径)」

    改了两天,终于将T1,T3毒瘤题改完了... T1 施工(单调栈优化DP) 考场上只想到了n*hmaxn*hmaxn的DP,用线段树优化一下变成n*hmaxn*log但显然不是正解 正解是很**的单调 ...

  9. LOJ #2769 -「ROI 2017 Day 1」前往大都会(单调栈维护斜率优化)

    LOJ 题面传送门 orz 斜率优化-- 模拟赛时被这题送走了,所以来写篇题解( 首先这个最短路的求法是 trivial 的,直接一遍 dijkstra 即可( 重点在于怎样求第二问.注意到这个第二问 ...

随机推荐

  1. springboot 配置文件说明

    你可以在自己创建的组件上使用@ConfigurationProperties注解,而Spring Boot自动配置的很多组件也添加了@ConfigurationProperties注解,可以通过Spr ...

  2. 在Visualforce页面中使用Visual Flow

    在本文中,我们将通过一个示例说明如何将"流"(Visual Flow)用于Visualforce页面. 更全面的知识可以参考官方文档. 创建流 我们要创建一个流,它的作用是得到一个 ...

  3. DNS协议总结

    1.DNS用于根据域名返回ip地址. 2.一般情况下,DNS-server是通过在UDP协议与客户端之间交互的,UDP端口号是53. 特别注意.DNS有时会使用TCP 53端口与客户端进行交互,所以, ...

  4. Angular基础(七) HTTP & Routing

    ​ 一.HTTP a)Angular提供了自己的HTTP库来调用外部API,为了能够在等待API响应的过程中继续与界面交互,采用异步HTTP请求的方式. b)Get请求,首先导入Http, Respo ...

  5. 程序员简单打造一个灵活智能的自动化运维系统C#实例程序

    你是一个程序员,被派去管理公司500台计算机.这些机器可能需要执行一些自动化任务,一台台手动操作会把你累死.重复性的工作还是交给电脑处理,怎么解决这个问题呢?一个自动化的运维系统是必须的.自己实现的好 ...

  6. list中放map的几种方式

    package Test; import java.util.*; public class Test { public static void main(String[] args) { //第一种 ...

  7. 【Java入门提高篇】Day22 Java容器类详解(五)HashMap源码分析(上)

    准备了很长时间,终于理清了思路,鼓起勇气,开始介绍本篇的主角——HashMap.说实话,这家伙能说的内容太多了,要是像前面ArrayList那样翻译一下源码,稍微说说重点,肯定会让很多人摸不着头脑,不 ...

  8. (网页)angular中实现li或者某个元素点击变色的两种方法(转)

    转自脚本之家: 本篇文章主要介绍了angular中实现li或者某个元素点击变色的两种方法,非常具有实用价值,需要的朋友可以参考下 本文介绍了angular中实现li或者某个元素点击变色的两种方法,分享 ...

  9. 将 Azure VM 迁移到 Azure 中的托管磁盘

    Azure 托管磁盘无需单独管理存储帐户,从而简化了存储管理. 还可以将现有的 Azure VM 迁移到托管磁盘,以便受益于可用性集中 VM 的更佳可靠性. 它可确保可用性集中不同 VM 的磁盘完全相 ...

  10. 32_使用BeanUtils工具包操作JavaBean

      由于对属性设置值和得到值的需求很多,使用频率很高,所以有一些开源勇士 不满足于JavaBean API 中IntroSpector来操作bean, 写出来了通用的BeanUtils工具,来进一步简 ...