problem
本题中,我们将用符号 ⌊ c ⌋ \lfloor c \rfloor ⌊c⌋ 表示对 c c c 向下取整,例如: ⌊ 3.0 ⌋ = ⌊ 3.1 ⌋ = ⌊ 3.9 ⌋ = 3 \lfloor 3.0 \rfloor = \lfloor 3.1 \rfloor = \lfloor 3.9 \rfloor = 3 ⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。
蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。
蛐蛐国里现在共有 n n n 只蚯蚓( n n n 为正整数)。每只蚯蚓拥有长度,我们设第 i i i 只蚯蚓的长度为 a i a_i ai( i = 1 , 2 , … , n i=1,2,\dots,n i=1,2,…,n),并保证所有的长度都是非负整数(即:可能存在长度为 0 0 0 的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数 p p p(是满足 0 < p < 1 0 < p < 1 0<p<1 的有理数)决定,设这只蚯蚓长度为 x x x,神刀手会将其切成两只长度分别为 ⌊ p x ⌋ \lfloor px \rfloor ⌊px⌋ 和 x − ⌊ p x ⌋ x - \lfloor px \rfloor x−⌊px⌋ 的蚯蚓。特殊地,如果这两个数的其中一个等于 0 0 0,则这个长度为 0 0 0 的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加 q q q(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要 m m m 秒才能到来。( m m m 为非负整数)
蛐蛐国王希望知道这 m m m 秒内的战况。具体来说,他希望知道:
- m m m 秒内,每一秒被切断的蚯蚓被切断前的长度(有 m m m 个数);
- m m m 秒后,所有蚯蚓的长度(有 n + m n + m n+m 个数)。
数据范围: n ≤ 1 0 5 n\le 10^5 n≤105, m ≤ 7 × 1 0 6 m\le 7\times 10^6 m≤7×106, t ≤ 71 t\le 71 t≤71, a i ≤ 1 0 8 a_i\le 10^8 ai≤108, q ≤ 200 q\le 200 q≤200。
solution
有一个比较显然的做法是用优先队列模拟这一过程。比较不好处理的是每条蚯蚓的增长,我们可以反着考虑,除了被切开的两条蚯蚓长度增加 q q q,可以看成被切开的两条蚯蚓长度减少 q q q,所以我们维护一个累计增加量,在被切开的两条蚯蚓上动手脚就可以了。
这样复杂度是 O ( m log n ) O(m\log n) O(mlogn) 的,可以拿到大约 80 80 80 分。
然后 100 100 100 分的做法就是注意到题目中本身的单调性,即先被切掉的蚯蚓分成的蚯蚓一定比后被切掉的蚯蚓分成的对应蚯蚓大。
证明如下:
有两只蚯蚓 a , b a,b a,b 且原长度 l a > l b l_a>l_b la>lb。
假设在某一时刻 a a a 被切割,此时 a a a 被分成了 p l a pl_a pla 和 l a ( 1 − p ) l_a(1-p) la(1−p) 两部分。
假设 t t t 秒后 b b b 被切割,此时 b b b 的长度是 l b + t q l_b+tq lb+tq,分成的两条蚯蚓就是 l b 1 = p ( l b + t q ) l_{b1}=p(l_b+tq) lb1=p(lb+tq), b b 2 = ( l b + t q ) ( 1 − p ) b_{b2}=(l_b+tq)(1-p) bb2=(lb+tq)(1−p)。而此时 l a 1 = p l a + t q l_{a1}=pl_a+tq la1=pla+tq, l a 2 = l a ( 1 − p ) + t q l_{a2}=l_a(1-p)+tq la2=la(1−p)+tq。容易得到 l a 1 > l b 1 l_{a1}>l_{b1} la1>lb1, l a 2 > l b 2 l_{a_2}>l_{b2} la2>lb2。
证毕。
因此我们维护三个队列,分别记录未被切割的蚯蚓,被分成 ⌊ p x ⌋ \lfloor px \rfloor ⌊px⌋ 段的蚯蚓和被分成 x − ⌊ p x ⌋ x-\lfloor px \rfloor x−⌊px⌋ 段的蚯蚓,在里面找最长的即可。
时间复杂度 O ( m ) O(m) O(m)。
code
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,q,u,v,t,a[100005];
queue<int>Q[4];
int main(){
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for(int i=n;i>=1;--i) Q[1].push(a[i]);
int del=0,tot=0;
for(int i=1;i<=m;++i){
int Max=-1000000000,p=0;
if(!Q[1].empty()&&Q[1].front()>Max) Max=Q[1].front(),p=1;
if(!Q[2].empty()&&Q[2].front()>Max) Max=Q[2].front(),p=2;
if(!Q[3].empty()&&Q[3].front()>Max) Max=Q[3].front(),p=3;
Q[p].pop(),Max+=del,del+=q;
if(i%t==0) printf("%d ",Max);
int L=1ll*Max*u/v,R=Max-L;
L-=del,R-=del;
Q[2].push(L),Q[3].push(R);
}puts("");
for(int i=1;i<=n+m;++i){
int Max=-1000000000,p=0;
if(!Q[1].empty()&&Q[1].front()>Max) Max=Q[1].front(),p=1;
if(!Q[2].empty()&&Q[2].front()>Max) Max=Q[2].front(),p=2;
if(!Q[3].empty()&&Q[3].front()>Max) Max=Q[3].front(),p=3;
Q[p].pop();
if(i%t==0) printf("%d ",Max+del);
}
return 0;
}