题面
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;
}