CF1239C Queue in the Train

(话说翻译还是我提交的,不过欢迎大家来踩)

比赛第一思路是用线段树,但是过于毒瘤导致没时间写完(虽然最后还是小组第一嘤嘤嘤)

考虑直接模拟

时间段很长,但是时间点很少,每个人的ti带来n个时间点,接水结束带来n个时间点,共2n个时间点。
由于本题中要求的全是最值(最靠左的。。最早的。。等等)
以及“判断有多少人到时间”也可以转化为“多次询问当前时间要求最小的人是否到时间,若到时间了就取出那个人”。反正全都是求最值的问题,既然是只要最值,那就上直接优先队列。

每次log模拟,再跳时间点,一共2n次,所以复杂度是nlogn

(这里用到了自己封装的一个对顶堆,支持删除的堆,待会再讲)

需要开的结构有:

一个堆,sitting,表示现在还未打算打水的人,按照键值为ti从小到大排序

一个堆,preparing,表示现在想要打水,但是不去排队的人,按照键值为pos(座位编号)从小到大排序

一个可删除堆,inque,表示现在正在排队的人,按照键值为pos从小到大排序

一个普通的队列,queorder,顾名思义,存的也是正在排队的人,不过不排序,也就是“谁先插入谁在前”。单纯模拟现在排队的队列。

由于考虑的是模拟,现在就来想想会遇到什么情况:

  1. 没有人排队,但是时间太早了以至于大家都坐着不想喝水。这个时候由于sitting中是按ti排序的,直接查看sitting.top().t并把当前时间(curtime)直接跳到sitting.top().t,接着不停地把sitting中到达curtime的节点(ti<=curtime)转移到preparing中,最后把preparing中座位最靠左的人转移到inquequeorder中。

此部分代码如下:

if(!inque.size()&&sitting.size())
{
	curtime=sitting.top().t;
	while(sitting.size()&&sitting.top().t<=curtime)
		preparing.push(sitting.top()),sitting.pop();
	inque.push(preparing.top()),queorder.push(preparing.top()),preparing.pop();
}
  1. 排在第一位的人正在接水,在他接水的这段时间内,有些人(这些人在sitting中)想要打水,分两种情况:

    ①在他之前(编号小于他)的人都坐着,这时由于sitting是按时间排序的,最先想打水的人可以直接起身去排队,所以我们就直接把这个人转移到inquequeorder中,而不用经过preparing

    ②在他之前(编号小于他)的座位有的空着,这时候即使他想打水也不会起身排队,于是把他丢到preparing

此部分代码如下:

while(sitting.size()&&sitting.top().t<=curtime)
	if(sitting.top().pos<inque.top().pos)
		inque.push(sitting.top()),queorder.push(sitting.top()),sitting.pop();
	else
		preparing.push(sitting.top()),sitting.pop();

(因为inque是将“正在排队的人”按照pos排过序了的,所以inque.top()就是现在正在排队的人中位置(座位)最靠左的人)

  1. 排在第一位的人打完水,回到座位,这个瞬间会有一些准备起身(preparing)人要起身打水。根据题目描述,多人打算同时起身时,最靠左的人会起身,其他的会坐下,所以直接把preparing.top()丢进inquequeorder

此部分代码如下:

inque.delete_(queorder.front()),queorder.pop();
while(sitting.size()&&sitting.top().t<=curtime)
	preparing.push(sitting.top()),sitting.pop();
if(preparing.size()&&(!inque.size()||preparing.top().pos<inque.top().pos))
	inque.push(preparing.top()),queorder.push(preparing.top()),preparing.pop();

while一下循环做上面三个步骤即可。。。

最后再介绍一下可删除堆(其实在给pos开个数组打标记也可,但是我不想这么做)其实就是另开一个“del”堆
保证∀x∈del,x∈que,这里的que指得是原堆。del中的元素就是要删除的元素,这样在查top的时候判一下,两个堆的对顶是否相等,若相等则两个都pop,一直pop到不等或del为空为止。

证明太水就不证了,下面就是整体代码:

#include<cstdio>
#include<queue>
typedef long long Int;
const int MaxN=100000,inf=0x3f3f3f3f;
inline void read(int &ans)
{
	ans=0;
	char c=getchar();
	while(c<'0'||c>'9')
		c=getchar();
	while(c>='0'&&c<='9')
		ans=ans*10+c-48,c=getchar();
	return;
}
int n,P;
struct Node
{
	int pos,t;
	friend inline bool operator == (const Node &a,const Node &b)
	{
		return a.pos==b.pos&&a.t==b.t;
	}
};
struct cmp_time
{
	inline bool operator () (const Node &a,const Node &b) const
	{
		return a.t==b.t?a.pos>b.pos:a.t>b.t;
	}
};
struct cmp_pos
{
	inline bool operator () (const Node &a,const Node &b) const
	{
		return a.pos>b.pos;
	}
};
template<typename Element,typename cmp>struct Exque
{
	private:
		std::priority_queue<Element,std::vector<Element>,cmp>que,del;
	public:
		inline int size()
		{
			return que.size()-del.size();
		}
		inline void pop()
		{
			if(del.size()&&que.top()==del.top())
				del.pop();
			return que.pop();
		}
		inline void push(const Element &x)
		{
			return que.push(x);
		}
		inline void delete_(const Element &x)
		{
			return que.top()==x?que.pop():del.push(x);
		}
		inline Element top()
		{
			while(del.size()&&que.top()==del.top())
				que.pop(),del.pop();
			return que.top();
		}
};
std::priority_queue<Node,std::vector<Node>,cmp_time>sitting;
std::priority_queue<Node,std::vector<Node>,cmp_pos>preparing;
Exque<Node,cmp_pos>inque;
std::queue<Node>queorder;
Int ans[MaxN+1];
int cnt=0;
int main()
{
//	freopen("test.in","r",stdin);
	int i,t;
	Int curtime=inf;
	read(n),read(P);
	for(i=1;i<=n;++i)
	{
		read(t),sitting.push((Node){i,t});
		if(t<curtime)
			curtime=t;
	}
	while(sitting.size()&&sitting.top().t<=curtime)
		preparing.push(sitting.top()),sitting.pop();
	if(preparing.size()&&(!inque.size()||preparing.top().pos<inque.top().pos))
		inque.push(preparing.top()),queorder.push(preparing.top()),preparing.pop();
	while(cnt<n)
	{
		++cnt,ans[queorder.front().pos]=(curtime+=P);
		while(sitting.size()&&sitting.top().t<=curtime)
			if(sitting.top().pos<inque.top().pos)
				inque.push(sitting.top()),queorder.push(sitting.top()),sitting.pop();
			else
				preparing.push(sitting.top()),sitting.pop();
		inque.delete_(queorder.front()),queorder.pop();
		while(sitting.size()&&sitting.top().t<=curtime)
			preparing.push(sitting.top()),sitting.pop();
		if(preparing.size()&&(!inque.size()||preparing.top().pos<inque.top().pos))
			inque.push(preparing.top()),queorder.push(preparing.top()),preparing.pop();
		if(!inque.size()&&sitting.size())
		{
			curtime=sitting.top().t;
			while(sitting.size()&&sitting.top().t<=curtime)
				preparing.push(sitting.top()),sitting.pop();
			inque.push(preparing.top()),queorder.push(preparing.top()),preparing.pop();
		}
	}
	for(i=1;i<=n;++i)
		printf("%I64d ",ans[i]);
	putchar('\n');
	return 0;
}
posted @ 2019-10-22 17:14  ZYyboT  阅读(245)  评论(0)    收藏  举报