洛谷luogu3957跳房子(单调队列优化)
QwQ被普及组的题折磨的死去活来。
硬是卡线段树,没卡过QwQ
oi生涯,第一道正经的单调队列dp题
进入正题
题目大意:
其中\(n \le 500000\)
看到这个题的第一感觉就是二分金币数
很显然,如果\(mid\)可以,那么\(mid+1\)一定可以
但是QwQ
不要想\(nlog^2n\)了(卡了一下午没卡过去)
首先考虑裸的\(dp\)
\(dp[i]=max(dp[j])+val[i]\)
其中\(mindis<=|dis[i]-dis[j]|<=maxdis\)
这种做法应该是\(O(n^2logn)\)的
这里就不放代码了
取max的过程可以用线段树来优化QwQ虽然和暴力一个分 但是我只写了这种暴力。。。放一波代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read()
{
   int x=0,f=1;char ch=getchar();
   while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
   while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
   return x*f;
}
const int maxn = 1000010;
int x[maxn],val[maxn];
int n,d,k;
int dp[maxn],f[4*maxn];
int add[4*maxn];
void up(int root)
{
    f[root]=max(f[2*root],f[2*root+1]);
}
void pushdown(int root,int l,int r)
{
    if (add[root])
    {
        add[2*root+1]=add[root*2]=add[root];
        f[2*root]=add[root];
        f[2*root+1]=add[root];
        add[root]=0;
    }
}
void build(int root,int l,int r)
{
    add[root]=0;
    if (l==r)
    {
        f[root]=-1;
        return;
    }
    int mid =(l+r) >> 1;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    up(root);
}
void update(int root,int l,int r,int x,int y,int p)
{
    if (l>r || x>y) return;
    if (x<=l && r<=y)
    {
        add[root]=p;
        f[root]=p;
        return;
    }
    int mid = (l+r) >>1;
    pushdown(root,l,r);
    if (x<=mid) update(2*root,l,mid,x,y,p);
    if (y>mid) update(2*root+1,mid+1,r,x,y,p);
    up(root);
}
int query(int root,int l,int r,int x,int y)
{
    if (l>r || x>y) return -1;
    if (x<=l && r<=y)
    {
        return f[root];
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    int ans=0;
    if (x<=mid) ans=max(ans,query(2*root,l,mid,x,y));
    if (y>mid) ans=max(ans,query(2*root+1,mid+1,r,x,y));
    return ans;
}
bool check(int min1,int max1)
{
    build(1,1,n);
    memset(f,-1,sizeof(f));
    memset(dp,-1,sizeof(dp));
    int l=1,r=1;
    //int beg=1;
    dp[1]=0;
    //cout<<1<<endl;
    for (int i=2;i<=n+1;i++)
    {
        //cout<<1<<endl;
        while (x[i]-x[r] >= min1) update(1,1,n,r,r,dp[r]),r++;
        while (x[i]-x[l]>max1) update(1,1,n,l,l,-1),l++;
        //if (l>r) continue;
        //cout<<1<<endl;
        int tmp = query(1,1,n,1,n);
        if (tmp!=-1)
        {
            dp[i]=max(dp[i],tmp+val[i]);
        }
    }
    int mx=-1e9;
    for (int i=1;i<=n+1;i++) mx=max(dp[i],mx);
    //for (int i=1;i<=n+1;i++) cout<<dp[i]<<" ";
   // cout<<endl;
    if (mx>=k) return true;
    else return false;
}
int main()
{
  n=read(),d=read(),k=read();
  for (int i=2;i<=n+1;i++) x[i]=read(),val[i]=read();
  int l=1,r=1e9;
  int ans=0;
  while (l<=r)
  {
  	 int mid =(l+r) >> 1;
  	 if (check(max(d-mid,1),d+mid)) r=mid-1,ans=mid;
  	 else l=mid+1;
  	 //cout<<l<<" "<<r<<endl;
  }
  if (ans==0) ans=-1;
  cout<<ans<<endl;
  return 0;
}
真正的正确做法 是使用单调队列优化
单调队列是什么呢?
是一个队列 对,队列中的元素都是单调的
就比如说\(3,2,1\)
单调队列在维护的时候有两个注意:
1.新进来元素,会将他之前连续的比他小的元素代替掉,直到遇到一个大的,或者队列为空 (可以理解为过期晚,决策更优秀)
2.注意\(head\)和\(tail\)的大小和加减
直接上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
inline ll read()
{
   ll x=0,f=1;char ch=getchar();
   while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
   while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
   return x*f;
}
const int maxn = 1000010;
ll x[maxn],val[maxn];
ll n,d,k;
ll dp[maxn];
ll q[maxn];
ll t[maxn];
ll l=1,r=1;
ll head=1,tail=0; 
void push(int now,ll min1,ll max1)
{
    while (x[now]-x[r]>=min1)
    {
        while (dp[r]>q[tail] && head<=tail) q[tail]=t[tail]=0,tail--;
        q[++tail]=dp[r];
        t[tail]=x[r];
        r++;
    }
    while (x[now]-t[head]>max1 && head<=tail) t[head]=0,q[head++]=0;
}
bool check(ll min1,ll max1)
{
    memset(dp,0xdf,sizeof(dp));
    l=1,r=1;
    dp[1]=0;
    head=1,tail=0;
    for (int i=2;i<=n+1;++i)
    {
        push(i,min1,max1);
        if (head<=tail)
        {
            dp[i]=max(dp[i],q[head]+val[i]);
        }
    }
    ll mx=-1e9;
    for (int i=1;i<=n+1;i++) mx=max(dp[i],mx);
    if (mx>=k) return true;
    else return false;
}
int main()
{
  n=read(),d=read(),k=read();
  for (int i=2;i<=n+1;i++) x[i]=read(),val[i]=read();
  ll l1=1,rr=6e9;
  ll ans=0;
  while (l1<=rr)
  {
  	 ll mid =(l1+rr) >> 1;
  	 if (check(max(d-mid,(long long)1),d+mid)) rr=mid-1,ans=mid;
  	 else l1=mid+1;
  	 //cout<<l<<" "<<r<<endl;
  }
  if (ans==0) ans=-1;
  cout<<ans<<endl;
  return 0;
}
												
											洛谷luogu3957跳房子(单调队列优化)的更多相关文章
- 滑动窗口-洛谷T1866(单调队列)
		
咕咕咕 单调队列板子题 一.基本 1.单调队列: 特殊的双端队列,内部元素.分为最大队列(单调递增)和最小队列(单调递减)两种 二.应用 本题中:大部分单调队列优化的动态规划问题都和定长连续子区间的最 ...
 - NOIP2017 PJ 跳房子 —— 单调队列优化DP
		
题目描述 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一.跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画n个格子,这些格子都在同一条直线上.每个格子内有一个 ...
 - 洛谷 U4792 Acheing 单调队列
		
U4792 Acheing 5通过 43提交 题目提供者Acheing 标签 难度尚无评定 提交 最新讨论 暂时没有讨论 题目背景 题目并没有什么含义,只是想宣传一下自己的博客,Acheing.com ...
 - 【洛谷】【单调队列】P2032 扫描
		
[题目描述:] 有一个 1 ∗ n 的矩阵,有 n 个正整数. 现在给你一个可以盖住连续的 k 的数的木板. 一开始木板盖住了矩阵的第 1 ∼ k 个数,每次将木板向右移动一个单位,直到右端与第 n ...
 - 洛谷P2827 蚯蚓(单调队列)
		
题意 初始时有$n$个蚯蚓,每个长度为$a[i]$ 有$m$个时间,每个时间点找出长度最大的蚯蚓,把它切成两段,分别为$a[i] * p$和$a[i] - a[i] * p$,除这两段外其他的长度都加 ...
 - P1091 合唱队形题解(洛谷,动态规划LIS,单调队列)
		
先上题目 P1091 合唱队形(点击打开题目) 题目解读: 1.由T1<...<Ti和Ti>Ti+1>…>TK可以看出这题涉及最长上升子序列和最长下降子序列 2 ...
 - 单调队列优化dp
		
洛谷p3800(单调队列优化DP) 题目背景 据说在红雾异变时,博丽灵梦单身前往红魔馆,用十分强硬的手段将事件解决了. 然而当时灵梦在Power达到MAX之前,不具有“上线收点”的能力,所以她想要知道 ...
 - 洛谷P3975 跳房子 [DP,单调队列优化,二分答案]
		
题目传送门 跳房子 题目描述 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一. 跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一 ...
 - 2018.09.26洛谷P3957 跳房子(二分+单调队列优化dp)
		
传送门 表示去年考普及组的时候失了智,现在看来并不是很难啊. 直接二分答案然后单调队列优化dp检验就行了. 注意入队和出队的条件. 代码: #include<bits/stdc++.h> ...
 
随机推荐
- ES6——静态属性与静态方法
			
静态方法只能写在class内,constructor外.通过static关键字声明 静态属性只能写在class外,通过 类名.属性名 = 属性值 声明 //静态属性与静态方法(ES6明确规定,Clas ...
 - canvas——离屏
			
离屏操作: 1.创建一个新的离屏canvas; 2.把一些复杂额绘画操作(背景),画在离屏canvas上: 3.将离屏canvas通过drawImage(离屏canvas对象,x1,y1,offcan ...
 - rasa  form的中断形式   自然机器语言学习   人工智能
			
Forms形式 最常见的对话模式之一是从用户那里收集一些信息以便做某事(预订餐厅.调用 API.搜索数据库等).这也称为**槽填充**. 用法# 要在 Rasa Open Source 中使用表单,您 ...
 - LVS实现(VS/DR)负载均衡和Keepalived高可用
			
LVS是Linux Virtual Server的简写即Linux虚拟服务器,是一个虚拟的服务器集群系统一组服务器通过高速的局域网或者地理分布的广域网相互连接,在它们的前端有一个负载调度器(Load ...
 - ubantu硬盘不足,无法启动
			
我的ubantu虚拟机经过我一顿操作后,就起不来了.然后经过多方询问,广集天下良方,最终发现是由于分配的硬件空间不足导致的.现象如下: 通过查看 root@ubantu:/snap# df -h Fi ...
 - openswan框架和编译时说明
			
刚开始学习openswan项目代码时,自己尝试了在虚拟机上编译.安装.运行openswan代码,由于当时刚开始学习openswan代码,因此对于其构成并不清楚,在编译.运行过程中有了问题,基本是通过百 ...
 - Optional容器类
			
一.Optional 容器类:用于尽量避免空指针异常 方法 /* * Optional.of(T t) : 创建一个 Optional 实例 * Optional.empty() : 创建一个空的 O ...
 - 使用Eclipse的基本配置
			
因本人 IntelliJ IDEA 正版授权前些日子已到期,最近开始使用 Eclipse .体验开发了一阵子,觉得除了在界面美观与前端编辑的操作上 Eclipse 与 IDEA 差距还比较大以外,其他 ...
 - TP5多条件搜索,同时有必要条件
			
$model = $this->model; // 查询是否有搜索参数 $search = input('?get.search') ? trim(input('get.search')) : ...
 - 一个完整的SEO优化方案
			
一个完整的SEO优化方案主要由四个小组组成: 一.前端/页编人员 二.内容编辑人员 三.推广人员 四.数据分析人员 接下来,我们就对这四个小组分配工作. 首先,前端/页编人员主要负责站内优化,主要从四 ...