Минимум на неизменяемом отрезке O(NlogN + K)



Дан массив из N целых чисел и K запросов, состоящих из двух индексов.
Необходимо для каждого запроса вывести минимальный из элементов, расположенных между индексами в запросе.

    Пусть M1[i][k] = min(a[j]), где i <= j <= i + 2k, и M2[i][k] = min(a[j]), где i - 2k <= j <= i, тогда минимум из элементов с i-того по j-тый будет равен min (M1[i][k], M2[j][k]), где k = floor(log2(j - i)).
    Осталось научиться вычислять матрицы M1 и M2. Пусть у нас есть минимум всех элементов с i-того по i + 2k, как нам найти минимум всех элементов с i + 1 по i + 1 + 2k? Для этого необходимо "добавить" в множество рассматриваемых элементов (i + 1 + 2k)-ый элемент и удалить i-тый элемент. Это очень похоже на операцию с очередью. Поэтому именно с помощью очереди с поддержкой минимума, используя её как движущееся окно, мы и вычислим наши матрицы.
    Оценим асимптотику. Грамотное вычисление матриц будет иметь сложность O(NlogN), а каждый запрос будет выполняться за O(1). Получаем O(NlogN + K).

Асимптотика O(NlogN + K).


Листинг C++

#include <vector>
#include <stack>
using namespace std;


class stack_min
{
private :
    class elem
    {
    public :
        int a, m;
        elem (int _a = 0, int _m = 0) : a (_a), m (_m) {};
    };
    stack < elem > s;
public :
    void pop ()
    {
        if (s.size ())
            s.pop ();
    }
    int size ()
    {
        return (int)s.size ();
    }
    int top ()
    {
        return s.top ().a;
    }
    int minimum ()
    {
        return s.size () <= 0 ? 1e9 : s.top ().m;
    }
    void push (int x)
    {
        s.push (elem (x, min (x, minimum ())));
    }
};

class queue_min
{
private :
    stack_min s1, s2;
public :
    void pop ()
    {
        if (s1.size ())
            s1.pop ();
        else
        {
            while (s2.size ())
            {
                s1.push (s2.top ());
                s2.pop ();
            }
            if (s1.size ())
                s1.pop ();
        }
    }
    int size ()
    {
        return s1.size () + s2.size ();
    }
    void push (int x)
    {
        s2.push (x);
    }
    int minimum ()
    {
        return min (s1.minimum (), s2.minimum ());
    }
};



class sparse_table
{
private :
    int n;
    vector < int > a;
    vector < int > b;
    vector < vector < int > > M1;
    vector < vector < int > > M2;
    vector < int > L;
public :
    sparse_table (vector < int > v)
    {
        int i, j;
        a = v;
        n = a.size ();
        b = a;
        for (i = 0; i < n; ++ i)
            b[i] = a[n - 1 - i];
        
        L.assign (n, 0);
        for (i = 0; i < n; ++ i)
            while ((1 << (L[i] + 1)) <= i)
                ++ L[i];
        int l = L[n - 1] + 1;
        
            
        M1.assign (n, vector < int > (l, 1e9));
        M2.assign (n, vector < int > (l, 1e9));
        vector < queue_min > Q1 (l);
        vector < queue_min > Q2 (l);
        for (j = 0; j < l; ++ j)
        {
            for (i = 0; i <= (1 << j); ++ i)
            {
                Q1[j].push (i < n ? a[i] : 1e9);
                Q2[j].push (i < n ? b[i] : 1e9);
            }
            M1[0][j] = Q1[j].minimum ();
            M2[0][j] = Q2[j].minimum ();
        }
        
        for (i = 1; i < n; ++ i)
            for (j = 0; j < l; ++ j)
            {
                Q1[j].pop ();
                Q1[j].push (i + (1 << j) < n ? a[i + (1 << j)] : 1e9);
                M1[i][j] = Q1[j].minimum ();
                
                Q2[j].pop ();
                Q2[j].push (i + (1 << j) < n ? b[i + (1 << j)] : 1e9);
                M2[i][j] = Q2[j].minimum ();
            }
        
    }
    
    int minimum (int i, int j)
    {
        if (i == j)    return a[i];
        return min (M1[i][L[j - i]], M2[n - 1 - j][L[j - i]]);
    }
};


14:58
05.08.2009


По всем вопросам обращаться: rumterg@gmail.com