【BZOJ1171】大sz的游戏(线段树+单调队列)
大致题意: 有\(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的游戏(线段树+单调队列)的更多相关文章
- 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP
2892: 强袭作战 Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 45 Solved: 30[Submit][Status][Discuss] D ...
- BZOJ 1012 线段树||单调队列
非常裸的线段树 || 单调队列: 假设一个节点在队列中既没有时间优势(早点入队)也没有值优势(值更大),那么显然不管在如何的情况下都不会被选为最大值. 既然它仅仅在末尾选.那么自然能够满足以上的条件 ...
- bzoj 1171 大sz的游戏& 2892 强袭作战 (线段树+单调队列+永久性flag)
大sz的游戏 Time Limit: 50 Sec Memory Limit: 357 MBSubmit: 536 Solved: 143[Submit][Status][Discuss] Des ...
- BZOJ1171: 大sz的游戏&BZOJ2892: 强袭作战
Description 大sz最近在玩一个由星球大战改编的游戏.话说绝地武士当前共控制了N个星球.但是,西斯正在暗处悄悄地准备他们的复仇计划.绝地评议会也感觉到了这件事.于是,准备加派绝地武士到各星球 ...
- POJ 2823 Sliding Window (线段树/单调队列)
题目不说了,可以用线段树或者单调队列,下面附上代码. 线段树: #include <iostream> #include <stdio.h> #include <algo ...
- 5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集
LINK:波波老师 LINK:同bzoj 1396 识别子串 不过前者要求线性做法 后者可以log过.实际上前者也被我一个log给水过了. 其实不算很水 我自认跑的很快罢了. 都是求经过一个位置的最短 ...
- loj #6302. 「CodePlus 2018 3 月赛」寻找车位【线段树+单调队列】
考虑静态怎么做:枚举右边界,然后枚举上边界,对应的下边界一定单调不降,单调栈维护每一列从当前枚举的右边界向左最长空位的长度,这样是O(nm)的 注意到n>=m,所以m<=2000,可以枚举 ...
- Codevs 4373 窗口(线段树 单调队列 st表)
4373 窗口 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description 给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只 ...
- 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]递增,所以可以维 ...
随机推荐
- UltraEdit 21.3 增加 mssql, json 高亮
1. %appdata%\IDMComp\UltraEdit 2. 将msql2k.uew, json.uew 放到 wordfiles 目录即可 msql2k json的uew 下载地址如 ...
- PLSQL Developer 客户端工具的安装
安装之前先把客户端工具instantclient_12_1拷贝到一个没有中文和空格的目录中去, 比如我直接放到了D:\tools下面.注意这里需要的是32bit的. 下面开始安装PLSQL Devel ...
- 移动开发:Android官方提供的支持不同屏幕大小的全部方法
转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/8830286 原文地址为:http://developer.android.com/ ...
- lua "诡异"的return用法
https://yq.aliyun.com/articles/11387 lua "诡异"的return用法 德哥 2016-03-29 15:38:42 浏览5690 评论0 ...
- 【Java】使用Eclipse进行远程调试,Linux下开启远程调试
原博地址:http://blog.csdn.net/dfdsggdgg/article/details/50730311 1.center下,在startup.sh文件首行中添加如下语句 declar ...
- Java入门系列-09-循环结构
这篇文章为你搞懂5个问题 while 循环如何使用 do-while 循环的使用 for 循环的使用 break.continue 的使用 循环结构的嵌套使用 生活中有很多事情需要我们重复的去做,比如 ...
- Java学习第二十三天
1:多线程(理解) (1)多线程:一个应用程序有多条执行路径 进程:正在执行的应用程序 线程:进程的执行单元,执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径 多进程 ...
- PHP常用数组操作方法汇总
array_change_key_case -- 返回字符串键名全为小写或大写的数组array_chunk -- 将一个数组分割成多个array_combine -- 创建一个数组,用一个数组的值作为 ...
- 2017年10月29日 数据库查询总结&45道题
日期函数: 当前时间:GetDate() 两个时间差:DateDiff() 一. 设有一数据库,包括四个表:学生表(Student).课程表(Course).成绩表(Score)以及教师信息表(Tea ...
- (二)HTML中的部分标签
HTML作为一种超文本标记语言,其中用到了大量的标签,昨天主要看了HTML中的图像标签和表格标签. (一)图像标签 <img> <img src="url"/&g ...