蚯蚓(队列)
原题链接
https://ac.nowcoder.com/acm/problem/16430
思路
这个题首先想到的用堆来维护,对于每一次操作,首先将当前的堆顶拿出来切成两半,再将两段插入堆中。这里堆的节点要记录这的点在0时刻的长度是多少,用来计算在第m秒的时候蚯蚓的长度。但是!但是一看数据范围,堆必定超时啊。 所以这里要改变解法,可以改为维护3个队列,第一个队列用来存最开始的所有蚯蚓,第二个用来维护切完后长的那段,第三个用来维护切完后短的那段,每一次取出三个队头中最大的那个,切断后存入队列,注意,这里存的是这只蚯蚓在0秒时的长度,例如一只长度为6的蚯蚓,p = 0.3,在第3秒切断,成长速度为1,那么他会被切成2和4两段,这里存2的时候,因为这只蚯蚓是第3秒出现的,那么在第0秒的时候长度应该为-1,这样存的话就可以在O(1)的时间计算出在这一刻这只蚯蚓的长度。
最后输出的时候有两种方法,第一种是将队列中的所有元素放入一个数组,排序后输出,第二种是像归并排序一样,用一个指针来每次输出最大值并弹出。
PS:可以证明,每一个队列中的元素都是单调递减的。
代码一:STL队列
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 100010;
queue<int> a, b, c;
int s[N];
bool cmp(LL a, LL b)
{
return a > b;
}
int main()
{
LL n, m, q, u, v, t;
cin >> n >> m >> q >> u >> v >> t;
for (int i = 0; i < n; i ++ ) scanf("%lld", &s[i]);
sort(s, s + n, cmp);
for (int i = 0; i < n; i ++ ) a.push(s[i]);
for (int i = 1; i <= m; i ++ )
{
LL grow = (i - 1) * q; // 长了多少
LL len =-0x3f3f3f3f;
int flag;
if (!a.empty() && a.front() > len) len = a.front(), flag = 1;
if (!b.empty() && b.front() > len) len = b.front(), flag = 2;
if (!c.empty() && c.front() > len) len = c.front(), flag = 3;
if (flag == 1) a.pop();
else if (flag == 2) b.pop();
else c.pop();
len += grow;
if (i % t == 0) printf("%lld ", len);
LL x1 = len * u / v;
LL x2 = len - x1;
// 将新的两段插入队列
if (x1 < x2) swap(x1, x2);
b.push(x1 - grow - q), c.push(x2 - grow - q);
}
puts("");
int cnt = 1;
while(1)
{
LL max_ = -0x3f3f3f3f;
int flag;
if (a.empty() && b.empty() && c.empty()) break;
if (!a.empty()) if(a.front() > max_) max_ = a.front(), flag = 1;
if (!b.empty()) if(b.front() > max_) max_ = b.front(), flag = 2;
if (!c.empty()) if(c.front() > max_) max_ = c.front(), flag = 3;
if (flag == 1) a.pop();
else if (flag == 2) b.pop();
else c.pop();
if (cnt % t == 0) printf("%lld ", max_ + m * q); // 计算出现在的长度
cnt ++ ;
}
return 0;
}
代码二:数组模拟队列
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 8000010; // 由于操作了m次,m是700万,可能出现蚯蚓数就是800万
LL a[N], b[N], c[N];
LL l1, r1 = -1, l2, r2 = -1, l3, r3 = -1; // 三个队列的指针
LL s[N];
bool cmp(LL a, LL b)
{
return a > b;
}
int main()
{
LL n, m, q, u, v, t;
cin >> n >> m >> q >> u >> v >> t;
for (int i = 0; i < n; i ++ ) scanf("%lld", &a[i]);
sort(a, a + n, cmp);
l1 = 0, r1 = n - 1; // 队头队尾指针
for (int i = 1; i <= m; i ++ )
{
int ll = (i - 1) * q; // 长了多长
int t1 = -0x3f3f3f3f, t2 = -0x3f3f3f3f, t3 = -0x3f3f3f3f;
// 先判断队列是否为空,不然这里可能出错
if (l1 <= r1) t1 = a[l1] + ll;
if (l2 <= r2) t2 = b[l2] + ll;
if (l3 <= r3) t3 = c[l3] + ll;
// 找出三个队列中队头的最大值,这里可以省去判断是否为空
int len = max(t1, max(t2, t3));
if (len == t1) l1 ++ ;
else if (len == t2) l2 ++ ;
else if (len == t3) l3 ++ ;
if (i % t == 0) printf("%lld ", len);
int x1 = (int)(len * u / v);
int x2 = len - x1;
if (x1 < x2) swap(x1, x2);
b[ ++ r2] = x1 - i * q, c[ ++ r3] = x2 - i * q;
}
puts("");
LL cnt = 0;
for (int i = l1; i <= r1; i ++ ) s[cnt ++ ] = a[i];
for (int i = l2; i <= r2; i ++ ) s[cnt ++ ] = b[i];
for (int i = l3; i <= r3; i ++ ) s[cnt ++ ] = c[i];
sort(s, s + cnt);
for (int i = cnt - 1, j = 1; i >= 0; i -- , j ++ )
if (j % t == 0)
printf("%lld ", s[i] + m * q);
cout << endl;
return 0;
}
浙公网安备 33010602011771号