蚯蚓(队列)

原题链接
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;
}
posted on 2021-04-28 21:31  Laurance  阅读(71)  评论(0)    收藏  举报