JZM 的国赛路(双端队列)

题面

JZM 要去参加国赛了!

JZM 为了把自己无意义的赛前复习时间消磨掉,打算取消航班,转为自驾奔赴考场。

但是他很快发现了一个问题:由于很多人听说了永神要自驾去考场,所以一路上很多车都慕名前来,打算一堵真容,这便造成了大拥堵。

有某一时刻 JZM 正堵在一条窄窄的单行道上。JZM 打开了芜德地图看了看,发现这条拥堵的单行道竟然长达 (5000000*轿车平均长度) 左右!

没人喜欢堵车,特别是老司机 JZM 。JZM 打算睡觉,但是当他前面的车走的时候,他就必须开动了,不然会后面的车骂。JZM 发现了一些规律,比如每辆车的速度几乎相等,每个司机的延迟差不多等等……于是 JZM 把这条单行道上前方的部分抽象成了一个长为 n 的序列,现在处于 0 时刻,每个位置上要么是空的,要么有一辆车,每个时刻每辆车都会进行如下操作:

  • 如果自己前面那个位置是空的,则自己往前走一个位置。

司机们都是同时操作,也就是说自己往前走一个位置的话,自己原先的位置要在下一个时刻才算空。这很符合常识,拥堵时所有车是不可能团结一心同时走的。

JZM 要睡 k 时刻,他想知道 k 时刻后整条道路是什么状况,每个位置是空的还是有车的。这个问题 JZM 肯定是随手切,但是他现在不想打代码,只想睡觉。于是他把这个问题丢给了你。

形式化的,给你一个初始的长为 n 的 0/1 序列,如果上一时刻某个 1 的前面是 0,那么这一时刻它就会与前面的 0 位置互换,求 k 时刻后的序列。

n <= 5000000

输入格式 :

n k
初始序列(length=n)

输出格式 :

答案序列(length=n)

题解

后面的 1 不会干扰前面的 1,但是如果后面的 1 被前面的 1 挡住了就会停车,前面变成 0 后才会继续前行,延迟为 1 。

那么我们不妨从前面的 1 推起,把所有的 1 的移动情况放到一个 x-t 图上,假设前一个 1 的图像长这样:

								| t=k 直线
								|
								|
								|
-----\							|
      \							|
       \						|
        \						|
         `---------\			|
    				\		   	|
    				 \		   	|
 					  `---- ...	| 

那么下一个 1 的图像会是怎样的呢 ?

首先他的起点在一开始的坐标,这没问题,然后会是这样:

\								| t=k 直线
 \								|
  \								|
   \							|
    \							|
     \							|
	  \							|
	   \						|
	    \						|
-----\   \						|
  ↑   \	  \						|
       \   \					|
        \   `-------\			|
         `---------\ \			|
    		  ↑		\ \		   	|
    				 \ \---- ...|
 					  `---- ...	| 

我们会发现,后面从某个地方起,他的图像相当于是上一个 1 的图像右平移 1 再上平移 1 !

而前面则是一段斜线,从起点延伸过来。

于是我们可以考虑维护这个图像,考虑到这个新点时就把上图第一个箭头指的那类所有平直线删掉,再把第二个箭头指的那段平直线修改一下,然后在开头加入新点的起点维护一下,后面的图像就用一个右移 tag 和一个上移 tag 维护,最后把尾部那些最左端大于 k 的平直线删掉,通过最右边的那段平直线可以算出这个 1 最终的位置。整个过程可以用一个双端队列来维护(建议手写)。

由于每次会删掉若干条平直线,最多只会加入一条平直线(起点),所以每个点的复杂度均摊 O(1),总体复杂度 O(n) 。

CODE

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 5000005
#define DB double
#define LL long long
#define ENDL putchar('\n')
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
int n,m,i,j,s,o,k;
int a[MAXN];
struct it{
	int l,r,h; it(){l = r = h = 0;}
	it(int L,int R,int H){l=L;r=R;h=H;}
}q[MAXN];
it operator + (it a,int b) {return it(a.l+b,a.r+b,a.h);}
int ad,ht;
int hd,tl;
int as[MAXN];
int ld(it x,int h) {
	return h-(x.h+ht)-1;
}
int main() {
	freopen("decrypt.in","r",stdin);
	freopen("decrypt.out","w",stdout);
	int LEN = read(); k = read();
	for(int i = 1;i <= LEN;i ++) {
		s = read();
		if(s == 1) {
			a[++ n] = i;
		}
	}
	q[hd = tl = 1] = it(0,k,0);
	for(int i = 1;i <= n;i ++) {
		if(a[i] == a[i-1]+1) {
			q[hd].l --;
			ad ++; ht ++;
		}
		else {
			while(hd >= tl && ld(q[hd]+ad,a[i]) > q[hd].r+ad) hd --;
			if(hd >= tl) {
				int ll = ld(q[hd]+ad,a[i]);
				q[hd].l = ll-ad-1;
			}
			ad ++; ht ++;
			q[++ hd] = it(-ad,-ad,a[i]-ht);
		}
		while(hd >= tl && q[tl].l+ad > k) tl ++;
		int res;
		if(q[tl].r+ad >= k) res = q[tl].h+ht;
		else res = q[tl].h+ht - (k-q[tl].r-ad);
		as[res] = 1;
	}
	for(int i = 1;i <= LEN;i ++) {
		printf("%d ",as[i]);
	}ENDL;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值