694. Minimum in the Queue

 

The input to a program is a set of operations with the queue. Each operation is either addition or removal of an item to or from the queue. After each operation find the smallest among all the numbers in a queue. Add up all the resulting numbers and get an answer. If after any operation the queue is empty, then add nothing to the answer. If it is impossible to remove an item because the queue is empty, then do nothing.

 

Input. The input data will be generated in your program. You will be given some parameters to get the input sequence.

The first input number n (1 ≤ n ≤ 106) is the number of operations with the queue. Then four nonnegative integers a, b, c, x0 are given. Their values are not greater than 10000.

Let's generate the input sequence x.

The first number of input sequence is x1. The first and each next number can be evaluated from the previous number using the formula:

xi = (a· + b·xi-1 + c) / 100 mod 106,

where '/' is an integer division, 'mod' is the remainder from division.

If xi mod 5 < 2, you must delete the number from the queue, otherwise add number xi to the queue.

 

Output. Print the resulting sum.

 

Sample input 1

Sample output 1

2 0 0 1 81

0

 

 

Sample input 2

Sample output 2

3 1 1 1 13

0

 

 

SOLUTION

data structures - queue

 

Algorithm analysis

In the problem you need to simulate the queue value. To answer the question about the minimum element in the queue in O(1), declare the queue of minimum elements mn. When element enqueues or dequeues, simulate the queue value in the common manner. Below we describe how the queue mn works:

·        When the element x enqueues, we shall delete from the mn tail the elements until they are greater than x. Then enqueue x into the tail of mn.

·        When the element dequeues, we shall delete the head from the queue mn only if it equals to the removed element.

During such simulation with queue mn the current minimum element of the queue value will usually be located at the head of the queue mn. And the elements of the queue mn will usually keep the sorted order.

If one use the data structure deque from standart template library, it is possible to get Time Limit. Simulate the work of both queues using static arrays.

 

Example

Lets simulate two queues using the next commands. We insert element to the end of the queue and remove from the head.

 

Algorithm realization

The queue value will contain the input numbers.

 

deque<int> value, mn;

 

Read the input data.

 

scanf("%d %d %d %d %d",&n,&a,&b,&c,&x);

for(i = 1; i <= n; i ++)

{

  x = ((1LL * a * x * x + 1LL * b * x + c) / 100) % 1000000LL;

  if (x % 5 < 2) // remove from the queue

  {

 

Remove the element from the queue. If the queue is empty, do nothing. If the head of the queue coinsides with the head of the queue mn (it is the case when the head of queue value is a minimum element in the queue, so after its removing the minimum element in the queue changes), delete both heads.

 

    if (!value.empty())

    {

      if (value.front() == mn.front()) mn.pop_front();

      value.pop_front();

    }

  } else  {

 

Enqueue the element x. Delete from the tail of the queue mn the elements greater than x. Then add x into the tail of queue mn.

 

    value.push_back(x);

    while(!mn.empty() && (x < mn.back())) mn.pop_back();

    mn.push_back(x);

  }

 

If the queue is not empty, its mimimum element is in the head of the queue mn.

 

 if (!mn.empty()) Res += mn.front();

}

 

Print the result – the sum of all minimum elements.

 

printf("%lld\n",Res);

 

Algorithm realization – class with STL

 

#include <cstdio>

#include <deque>

using namespace std;

 

int x, i, n, a, b, c;

long long Res = 0;

 

class Deque

{

private:

  deque<int> q, min;

public:

  void pop(void)

  {

    if (!q.empty())

    {

      if (q.front() == min.front()) min.pop_front();

      q.pop_front();

    }

  }

 

  int getMin(void)

  {

    return (min.empty()) ? 0 : min.front();

  }

 

  void push(int x)

  {

    q.push_back(x);

    while(!min.empty() && x < min.back()) min.pop_back();

    min.push_back(x);

  }

};

 

int main(void)

{

  scanf("%d %d %d %d %d",&n,&a,&b,&c,&x);

  Deque d;

  for(i = 1; i <= n; i ++)

  {

    x = ((1LL * a * x * x + 1LL * b * x + c) / 100) % 1000000LL;

    if (x % 5 < 2)

      d.pop(); // remove from the deque

    else

      d.push(x); // add value x into the deque

    Res += d.getMin();

  }

  printf("%lld\n",Res);

  return 0;

}

 

Algorithm realization – arrays

The program works faster when queue is implementated on arrays rather than using class deque of STL.

Declare the deques value and mn. Variables Bvalue and Evalue are the pointers to the start and to the end of deque value. Variables Bmn and Emn are the pointers to the start and to the end of deque mn. The result is calculated in the variable Res. The number of operations with deque is no more than 106, so we can use arrays of this size.

 

#define MAX 1000010

int value[MAX], mn[MAX];

int Bvalue, Evalue, Bmn, Emn;

long long Res = 0;

 

Main part of the program. Read the input data. Initialize the pointers to the start and to the end of the deques.

 

scanf("%d %d %d %d %d",&n,&a,&b,&c,&x);

Bvalue = Evalue = Bmn = Emn = 0;

for(i = 1; i <= n; i ++)

{

  x = ((1LL * a * x * x + 1LL * b * x + c) / 100) % 1000000LL;

  if (x % 5 < 2) // remove from the deque

  {

    if (Bvalue != Evalue)

    {

      if (value[Bvalue] == mn[Bmn]) Bmn++;

      Bvalue++;

    }

  } else // add x into the deque

  {

    value[Evalue++] = x;

    while((Bmn != Emn) && (x < mn[Emn-1])) Emn--;

    mn[Emn++] = x;

  }

 if (Bmn != Emn) Res += mn[Bmn];

}

 

printf("%lld\n",Res);

 

Algorithm realization - class

 

#include <stdio.h>

 

int x, i, n, a, b, c;

long long Res = 0;

 

class Deque

{

public:

  int *value, *mn;

  int Bvalue, Evalue, Bmn, Emn;

 

  Deque(int n = 1000010)

  {

    value = new int[n];

    mn = new int[n];

    Bvalue = Evalue = Bmn = Emn = 0;

  }

 

  ~Deque()

  {

    delete[] value;

    delete[] mn;

  }

 

  void pop(void)

  {

    if (Bvalue != Evalue)

    {

      if (value[Bvalue] == mn[Bmn]) Bmn++;

      Bvalue++;

    }

  }

 

  int getMin(void)

  {

    return (Bmn != Emn) ? mn[Bmn] : 0;

  }

 

  void push(int x)

  {

    value[Evalue++] = x;

    while((Bmn != Emn) && (x < mn[Emn-1])) Emn--;

    mn[Emn++] = x;

  }

};

 

int main(void)

{

  scanf("%d %d %d %d %d",&n,&a,&b,&c,&x);

  Deque d(n);

  for(i = 1; i <= n; i ++)

  {

    x = ((1LL * a * x * x + 1LL * b * x + c) / 100) % 1000000LL;

    if (x % 5 < 2)

      d.pop(); // remove from the deque

    else

      d.push(x); // add x into the deque

    Res += d.getMin();

  }

  printf("%lld\n",Res);

  return 0;

}

 

Algorithm realization – linked list

The implementation of deque with pointers is advantageous because at each moment of time we use exactly as much memory as we need to store all the data.

 

#include <stdio.h>

 

class deque

{

private:

  struct node

  {

    int data;

    node *next, *prev;

 

    node()

    {

      prev = next = NULL;

    }

 

    node(int a)

    {

      data = a;

      prev = next = NULL;

    }

  } *Head, *Tail;

 

public:

  deque()

  {

    Head = Tail = NULL;

  }

 

  void push_back(int a)

  {

    node *p = new node(a);

    if(Head == NULL)

      Head = Tail = p;

    else

    {

      p->prev = Tail;

      p->next = NULL;

      Tail->next = p;

      Tail = p;

    }

  }

 

  void push_front(int a)

  {

    node *p = new node(a);

    if(Head == NULL)

      Head = Tail = p;

    else

    {

      p->next = Head;

      p->prev = NULL;

      Head->prev = p;

      Head = p;

    }

  }

 

  int pop_front(void)

  {

    node *p = Head;

    int r = Head->data;

    if(Head == Tail)

      Head = Tail = NULL;

    else

    {

      Head = Head->next;

      Head->prev = NULL;

    }

    delete p;

    return r;

  }

 

  int pop_back(void)

  {

    node *p = Tail;

    int r = Tail->data;

    if(Head == Tail)

      Head = Tail = NULL;

    else

    {

      Tail = Tail->prev;

      Tail->next = NULL;

    }

    delete p;

    return r;

  }

 

  int empty(void)

  {

    return Head == NULL;

  }

 

  int front(void)

  {

    return Head->data;

  }

 

  int back(void)

  {

    return Tail->data;

  }

};

 

deque value, mn;

int x, i, n, a, b, c;

long long Res = 0;

 

int main(void)

{

  scanf("%d %d %d %d %d",&n,&a,&b,&c,&x);

  for(i = 1; i <= n; i ++)

  {

    x = ((1LL * a * x * x + 1LL * b * x + c) / 100) % 1000000LL;

    if (x % 5 < 2) // remove from the deque

    {

      if (!value.empty())

      {

        if (value.front() == mn.front()) mn.pop_front();

        value.pop_front();

      }

    } else // add value x into the deque

    {

      value.push_back(x);

      while(!mn.empty() && (x < mn.back())) mn.pop_back();

      mn.push_back(x);

    }

   if (!mn.empty()) Res += mn.front();

  }

  printf("%lld\n",Res);

  return 0;

}

 

Java realization

 

import java.util.*;

 

public class Main

{

  public static void main(String[] args)

  {

    Scanner con = new Scanner(System.in);

    LinkedList<Long> value = new LinkedList<Long>();

    LinkedList<Long> mn = new LinkedList<Long>();

    long n = con.nextLong(), a = con.nextLong();

    long b = con.nextLong(), c = con.nextLong();

    long x = con.nextLong(), Res = 0;

    for(long i = 1; i <= n; i ++)

    {

      x = ((a * x * x + b * x + c) / 100) % 1000000;

      if (x % 5 < 2)

      {

        if (value.isEmpty() == false)

        {

          if (value.getFirst().equals(mn.getFirst())) mn.removeFirst();

          value.removeFirst();

        }

      } else

      {

        value.addLast(x);

        while(!mn.isEmpty() && (x < mn.getLast())) mn.removeLast();

        mn.addLast(x);

      }

     if (!mn.isEmpty()) Res += mn.getFirst();

    }

    System.out.println(Res);

   }

}