点此看题面

大致题意: 有\(n\)个点,两点间最大通讯距离为\(L\)。已知除\(1\)号点外第\(i\)个点能够发出和接收的信号区间\([l_i,r_i]\)以及到\(1\)号点的距离\(dis_i\)(\([l_1,r_1]\)为\([0,INF]\))。对于两个点\(i,j\),只有满足\(i<j\),\([l_i,r_i]\)与\([l_j,r_j]\)有交集且\(dis_j-dis_i\le L\)时,才会有一条从\(j\)到\(i\)的有向边。求每个点到\(1\)号点的最短距离(无法到达输出\(-1\))。

一个暴力的单调队列做法

首先,对于这道题应该比较容易想到用单调队列去搞。

我们考虑对于每一种信号波长开一个单调队列(离散化之后就只有\(2n\)个),用来记录对这种波长有影响的点的编号,维护这些点的答案单调递增(因为后加入的肯定可以覆盖先加入的较为不优的答案)。

初始化,在每个单调队列里扔一个\(1\)。

下一步,我们从\(2\)到\(n\)枚举\(i\)。

对于当前的\(i\),我们首先枚举每个队列,将队首的几个\(dis_i-dis_x>L\)的数给弹掉(因为队列中的元素显然递增,所以\(dis\)值也递增,故只需处理队首)。

然后,我们在\([l_i,r_i]\)范围内的所有单调队列中,求出它们队首的最小值\(k\)。

则\(ans_i\)即为\(k+1\)。

接下来,我们再把\(i\)给扔入\([l_i,r_i]\)范围内的所有单调队列中。

这样就完成了对于一个点的操作。

不难发现这样显然会\(T\)飞,因此需要优化。

线段树优化(标记永久化)

看到区间询问队首最小值、区间扔数,我们应该比较容易想到线段树

然而,只对每个叶子节点各开一个单调队列,显然是不好维护的,每次加入的数的规模依然是\(O(n)\)。

那么我们就要用到一个比较实用的线段树优化技巧:标记永久化

在这题中,这个优化的具体实现就是,对于每一个非叶子节点,我们也开一个单调队列,来表示这棵子树内所有叶节点的单调队列都需要加入这个单调队列中的数

然后一次修改就只需要加入\(O(logn)\)个数。

而询问,我们记下来每个节点子树内的队首最小值\(Mn_i\),然后对于第\(i\)个点,它实际的队首最小值就是\(Mn_i\)与到根节点路径上所有祖先节点的队首最小值中的较小值。

后者可以在从上往下查询的时候顺带统计出来。

具体实现可以详见代码。

关于\(STL\)的\(deque\)与\(list\)

由于要给每个点开单调队列,不会写指针的我就只能借助\(STL\)的双端队列\(deque\)了。

然而,关于\(deque\),它\(MLE\)了。。。

可更神奇的是,当我把\(deque\)换成\(list\),而其他地方原封不动,却过了!

关于这一点,我和\(hl666\)通过猜测,得出了一个似乎较为合理的结论:

  • \(deque\)是用迭代器实现的,所以内存大,而它的优势在于可以使用\(iterator\)进行遍历。
  • \(list\)应该是用指针实现的,所以内存小。

也算是涨知识了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 250000
#define INF 1e9
#define Gmin(x,y) (x>(y)&&(x=(y)))
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define min(x,y) ((x)<(y)?(x):(y))
#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
using namespace std;
int n,m,dc,l[N+5],r[N+5],dis[N+5],ans[N+5],dv[(N<<1)+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {x<0&&(pc('-'),x=-x);W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
I void clear() {fwrite(FO,1,C,stdout),C=0;}
}F;
class MonQueue//单调队列
{
private:
list<int> q;
public:
I MonQueue() {q.clear();}I int Front() {return q.empty()?INF:ans[q.front()];}//求队首,为空时返回INF
I void Pop(CI x) {!q.empty()&&!(q.front()^x)&&(q.pop_front(),0);}//若有,则弹出值为x的元素
I void Push(CI x) {W(!q.empty()&&ans[q.back()]>=ans[x]) q.pop_back();q.push_back(x);}//加入一个新元素,注意维护答案单调递增
};
class SegmentTree//线段树
{
private:
#define SZ 1048573//卡内存,实践可得用到的叶节点中最大编号为1048573
#define STO l,hl,rt<<1,tl,tr
#define ORZ hl+1,r,rt<<1|1,tl,tr
#define PU(x) (O[x].Mn=O[x].Q.Front(),(x<<1)<=Mx&&(Gmin(O[x].Mn,O[x<<1].Mn),Gmin(O[x].Mn,O[x<<1|1].Mn)))//特判左、右儿子编号超过最大编号时不上传信息,也用于卡内存
int Mx;struct node {int Mn;MonQueue Q;I node() {Mn=INF;}}O[SZ+5];
I void ins(CI l,CI r,CI rt,CI tl,CI tr,CI v)//插入
{
if(tl<=l&&r<=tr) return (void)(O[rt].Q.Push(v),PU(rt));RI hl=l+r>>1;
tl<=hl&&(ins(STO,v),0),tr>hl&&(ins(ORZ,v),0),PU(rt);
}
I void del(CI l,CI r,CI rt,CI tl,CI tr,CI v)//删除
{
if(tl<=l&&r<=tr) return (void)(O[rt].Q.Pop(v),PU(rt));RI hl=l+r>>1;
tl<=hl&&(del(STO,v),0),tr>hl&&(del(ORZ,v),0),PU(rt);
}
I int qry(CI l,CI r,CI rt,CI tl,CI tr)//询问
{
if(tl<=l&&r<=tr) return O[rt].Mn;RI hl=l+r>>1,res=O[rt].Q.Front(),t;
return tl<=hl&&(t=qry(STO),Gmin(res,t)),tr>hl&&(t=qry(ORZ),Gmin(res,t)),res;
}
public:
I void Init(CI l=1,CI r=dc,CI rt=1)//初始化
{
if(Gmax(Mx,rt),!(l^r)) return;RI hl=l+r>>1;
Init(l,hl,rt<<1),Init(hl+1,r,rt<<1|1);
}
I void Insert(CI x) {ins(1,dc,1,l[x],r[x],x);}I void Delete(CI x) {del(1,dc,1,l[x],r[x],x);}
I int Query(CI x) {return qry(1,dc,1,l[x],r[x]);}
}S;
int main()
{
RI i,p=1;for(F.read(n,m),i=2;i<=n;++i) F.read(l[i],r[i],dis[i]),dv[(i-1<<1)-1]=l[i],dv[i-1<<1]=r[i];
sort(dv+1,dv+(n-1<<1)+1),dc=unique(dv+1,dv+(n-1<<1)+1)-dv-1;//离散化
for(l[1]=1,r[1]=dc,S.Init(),S.Insert(1),i=2;i<=n;++i)//初始化+枚举i
{
l[i]=GV(l[i]),r[i]=GV(r[i]);W(dis[i]-dis[p]>m) S.Delete(p++);//弹出队首不符合条件的数(由于被弹掉的数必定从1开始每次递增1,因此可以直接开个变量存储)
F.writeln((ans[i]=S.Query(i)+1)>=INF?-1:ans[i]),S.Insert(i);//求解并输出答案,然后扔入单调队列
}return F.clear(),0;
}

【BZOJ1171】大sz的游戏(线段树+单调队列)的更多相关文章

  1. 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP

    2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] D ...

  2. BZOJ 1012 线段树||单调队列

    非常裸的线段树  || 单调队列: 假设一个节点在队列中既没有时间优势(早点入队)也没有值优势(值更大),那么显然不管在如何的情况下都不会被选为最大值. 既然它仅仅在末尾选.那么自然能够满足以上的条件 ...

  3. bzoj 1171 大sz的游戏& 2892 强袭作战 (线段树+单调队列+永久性flag)

    大sz的游戏 Time Limit: 50 Sec  Memory Limit: 357 MBSubmit: 536  Solved: 143[Submit][Status][Discuss] Des ...

  4. BZOJ1171: 大sz的游戏&BZOJ2892: 强袭作战

    Description 大sz最近在玩一个由星球大战改编的游戏.话说绝地武士当前共控制了N个星球.但是,西斯正在暗处悄悄地准备他们的复仇计划.绝地评议会也感觉到了这件事.于是,准备加派绝地武士到各星球 ...

  5. POJ 2823 Sliding Window (线段树/单调队列)

    题目不说了,可以用线段树或者单调队列,下面附上代码. 线段树: #include <iostream> #include <stdio.h> #include <algo ...

  6. 5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集

    LINK:波波老师 LINK:同bzoj 1396 识别子串 不过前者要求线性做法 后者可以log过.实际上前者也被我一个log给水过了. 其实不算很水 我自认跑的很快罢了. 都是求经过一个位置的最短 ...

  7. loj #6302. 「CodePlus 2018 3 月赛」寻找车位【线段树+单调队列】

    考虑静态怎么做:枚举右边界,然后枚举上边界,对应的下边界一定单调不降,单调栈维护每一列从当前枚举的右边界向左最长空位的长度,这样是O(nm)的 注意到n>=m,所以m<=2000,可以枚举 ...

  8. Codevs 4373 窗口(线段树 单调队列 st表)

    4373 窗口 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description 给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只 ...

  9. BZOJ1171 : 大sz的游戏

    f[i]=min(f[j])+1,线段j与线段i有交,且l[i]-l[j]<=L. 线段j与线段i有交等价于y[j]>=x[i],x[j]<=y[i]. 因为l[i]递增,所以可以维 ...

随机推荐

  1. c# 屏蔽快捷键

    前言 有时候开发会遇到这样一个需求,软件需要屏蔽用户的组合快捷键或某些按键,避免强制退出软件,防止勿操作等. 原理 1.要实现组合键,按键拦截,需要用到user32.dll中的SetWindowsHo ...

  2. apache CXF quickstart

    1下载 官网: cxf.apache.org 下载 CXF 的开发包: 解压上面的 zip 文件 : 2介绍 1什么是cxf Apache CXF™ is an open source service ...

  3. java 读写操作大文件 BufferedReader和RandomAccessFile

    一 老问这问题,两个都答出来算加分项? 二 具体代码如下,没什么好说的直接说对比. BufferedReader和RandomAccessFile的区别RandomAccessFile 在数据越大,性 ...

  4. JS获取鼠标位置,兼容IE FF

    由于Firefox和IE等浏览器之间对js解释的方式不一样,firefox下面获取鼠标位置不能够直接使用clientX来获取.网上说的一般都是触发mousemove事件才行.我这里有两段代码,思路都一 ...

  5. Hashtable 键值对集合

    // Hashtable  键值对集合 一个键对应一个值 Hashtable ht=new Hashtable(); ht.Add(,"张三"); ht.Add(,'男'); ht ...

  6. js中判断对象是否存在

    s中判断对象是否存在,写法有很多种: 第一种:if (!myObj) { var myObj = { }; }第二种:var global = this;  if (!global.myObj) {  ...

  7. mvc 中Request[""]与Request.QueryString[""]

    1.Request[""]与Request.QueryString[""]获取不到值时返回null: 2.Request[""]与Reque ...

  8. java程序: 从kernel.ubuntu.com下载kernel - HttpURLConnection

    用java实现从kernel.ubuntu.com下载内核的小工具 现在的最新的LTS版本是4.19,目前已经更新到4.19.13了. Kernel的更新通知里,经常有下面的陈述: 作为一个听劝的人, ...

  9. Java ConcurrentHashMap的小测试

    今天正式开始自己的分布式学习,在第一章介绍多线程工作模式时,作者抛出了一段关于ConcurrentHashMap代码让我很是疑惑,代码如下: public class TestClass { priv ...

  10. Stage2--Python的数据类型

    说在前面: Stage1-Stage4简单介绍一下Python语法,Stage5开始用python实现一些实际应用,语法的东西到处可以查看到,学习一门程序语言的最终目的是应用,而不是学习语法,语法本事 ...