题目大意:
一共有n只蚯蚓,每只蚯蚓有一个长度,并且蚯蚓会按每年q厘米的速度增长。现在每年选择一只最长的蚯蚓,将其按p的比例切成两半,被切的的蚯蚓这一年不能增长,问每年被切的蚯蚓的长度和m年后所有蚯蚓的长度(据题意只需输出部分结果)
题目分析:
看到这题首先想到的是使用堆来做(因为,切蚯蚓符合堆的查询极值、插入和删除)。但考虑到数据的范围( 1 < = n < = 1 0 5 1<=n<=10^5 1<=n<=105, 0 < = m < = 7 ∗ 1 0 6 0<=m<=7*10^6 0<=m<=7∗106),由于使用堆的时间复杂度为O(m*logn),其结果必然会导致超时,所以只有另辟蹊径。
再仔细观察,发现每年切最长的一只蚯蚓,说明切过的蚯蚓长度具有单调性,即先切的蚯蚓的左段一定大于等于后切蚯蚓的左段,先切蚯蚓的右段一定大于等于后切蚯蚓的右段。
以下为证明过程:为方便,设两只蚯蚓长度为a,b,切后的长度分别是a1,a2,b1,b2.
- a>=b
- 所以 ap>=bp,a-ap>=b-bp
- 因为切这两只蚯蚓的时候这两只蚯蚓都少增长了q厘米
- 所以可证a1>=b1,a2>=b2
因此我们可以想到这题可以使用普通队列来做,使复杂度降至O(m) - 使用3个队列分别保存没被切过的蚯蚓,被切过的蚯蚓左段,和被切过的蚯蚓右段
注意因为最开始给的蚯蚓数据并不是按照递减顺序,所以可以选择提前排序或者第一个队列使用优先队列 - 每次取出3个队列中最长的一只蚯蚓,将其切断都分别放入后两个数组,依此类推循环m次即可求出结果
- 还需要考虑蚯蚓的增长长度问题,由于每次除了被切的蚯蚓,其他所有的蚯蚓都在增长,所以只需要再从队列中取元素的时候加上(i-1)q,放回时减去iq的长度(因为被切的蚯蚓这年不增长,所以多减q厘米,具体实现参见程序)
#include<bits/stdc++.h>
using namespace std;
const int inf=-0x3f3f3f3f;
int n,m,q,u,v,t,in[100005],delta;
double p;
queue<int> a,b,c;
void read() {
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
p=u*1.0/v;
for(int i=1;i<=n;i++) {
scanf("%d",&in[i]);
}
sort(in+1,in+1+n);
for(int i=n;i>=1;i--) {
a.push(in[i]);
}
}
void work() {
for(int i=1;i<=m;i++) {
int x=inf,y,z,out;
if(a.size()) x=a.front();
if(i==1) {
a.pop();
x+=delta;
out=x;
b.push(int(x*p)-delta-q);c.push(x-int(x*p)-delta-q);
}
else {
y=b.front(),z=c.front();
if(x>=y&&x>=z) {
a.pop();
x+=delta;
out=x;
b.push(int(x*p)-delta-q);c.push(x-int(x*p)-delta-q);
}
else if(y>=x&&y>=z) {
b.pop();
y+=delta;
out=y;
b.push(int(y*p)-delta-q);c.push(y-int(y*p)-delta-q);
}
else {
c.pop();
z+=delta;
out=z;
b.push(int(z*p)-delta-q);c.push(z-int(z*p)-delta-q);
}
}
if(i%t==0) printf("%d ",out);
delta+=q;
}
}
void print() {
for(int i=1;i<=n+m;i++) {
int x=inf,y=inf,z=inf,out;
if(a.size()) x=a.front();
if(b.size()) y=b.front();
if(c.size()) z=c.front();
if(x>=y&&x>=z) {
out=x;
a.pop();
}
else if(y>=x&&y>=z) {
out=y;
b.pop();
}
else {
out=z;
c.pop();
}
if(i%t==0) printf("%d ",out+delta);
}
}
int main() {
read();
work();
printf("\n");
print();
}