【NOIP 2016 提高组】蚯蚓

传送门


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 xpx 的蚯蚓。特殊地,如果这两个数的其中一个等于 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 n105 m ≤ 7 × 1 0 6 m\le 7\times 10^6 m7×106 t ≤ 71 t\le 71 t71 a i ≤ 1 0 8 a_i\le 10^8 ai108 q ≤ 200 q\le 200 q200


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(1p) 两部分。
假设 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)(1p)。而此时 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(1p)+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 xpx 段的蚯蚓,在里面找最长的即可。

时间复杂度 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值